Scalaのリファクタリングに関するアドバイス-foreachループで使用されている変数を削除できますか

ジャミエット

Scalaコードをリファクタリングして、よりエレガントで慣用的なScalaにする方法についてのアドバイスを探しています。

機能があります

def joinDataFramesOnColumns(joinColumns: Seq[String]) : org.apache.spark.sql.DataFrame

Seq[org.apache.spark.sql.DataFrame]それらを結合することにより、で動作しますjoinColumns関数の定義は次のとおりです。

implicit class SequenceOfDataFrames(dataFrames: Seq[DataFrame]){
    def joinDataFramesOnColumns(joinColumns: Seq[String]) : DataFrame = {
      val emptyDataFrame = SparkSession.builder().getOrCreate().emptyDataFrame
      val nonEmptyDataFrames = dataFrames.filter(_ != emptyDataFrame)
      if (nonEmptyDataFrames.isEmpty){
        emptyDataFrame
      }
      else {
        if (joinColumns.isEmpty) {
          return nonEmptyDataFrames.reduce(_.crossJoin(_))
        }
      nonEmptyDataFrames.reduce(_.join(_, joinColumns))
    }
  }
}

すべて成功するユニットテストがいくつかあります。

class FeatureGeneratorDataFrameExtensionsTest extends WordSpec {
  val fruitValues = Seq(
    Row(0, "BasketA", "Bananas", "Jack"),
    Row(2, "BasketB", "Oranges", "Jack"),
    Row(2, "BasketC", "Oranges", "Jill"),
    Row(3, "BasketD", "Oranges", "Jack"),
    Row(4, "BasketE", "Oranges", "Jack"),
    Row(4, "BasketE", "Apples", "Jack"),
    Row(4, "BasketF", "Bananas", "Jill")
  )
  val schema = List(
    StructField("weeksPrior", IntegerType, true),
    StructField("basket", StringType, true),
    StructField("Product", StringType, true),
    StructField("Customer", StringType, true)
  )
  val fruitDf = spark.createDataFrame(
    spark.sparkContext.parallelize(fruitValues),
    StructType(schema)
  ).withColumn("Date", udfDateSubWeeks(lit(dayPriorToAsAt), col("weeksPrior")))

  "FeatureGenerator.SequenceOfDataFrames" should {
    "join multiple dataframes on a specified set of columns" in {
      val sequenceOfDataFrames = Seq[DataFrame](
        fruitDf.withColumnRenamed("weeksPrior", "weeksPrior1"),
        fruitDf.withColumnRenamed("weeksPrior", "weeksPrior2"),
        fruitDf.withColumnRenamed("weeksPrior", "weeksPrior3"),
        fruitDf.withColumnRenamed("weeksPrior", "weeksPrior4"),
        fruitDf.withColumnRenamed("weeksPrior", "weeksPrior5")
      )
      val joinedDataFrames = sequenceOfDataFrames.joinDataFramesOnColumns(Seq("basket", "Product", "Customer", "Date"))
      assert(joinedDataFrames.columns.length === 9)
      assert(joinedDataFrames.columns.contains("basket"))
      assert(joinedDataFrames.columns.contains("Product"))
      assert(joinedDataFrames.columns.contains("Customer"))
      assert(joinedDataFrames.columns.contains("Date"))
      assert(joinedDataFrames.columns.contains("weeksPrior1"))
      assert(joinedDataFrames.columns.contains("weeksPrior2"))
      assert(joinedDataFrames.columns.contains("weeksPrior3"))
      assert(joinedDataFrames.columns.contains("weeksPrior4"))
      assert(joinedDataFrames.columns.contains("weeksPrior5"))
    }
    "when passed a list of one dataframe return that same dataframe" in {
      val sequenceOfDataFrames = Seq[DataFrame](fruitDf)
      val joinedDataFrame = sequenceOfDataFrames.joinDataFramesOnColumns(Seq("basket", "Product"))
      assert(joinedDataFrame.columns.sorted === fruitDf.columns.sorted)
      assert(joinedDataFrame.count === fruitDf.count)
    }
    "when passed an empty list of dataframes return an empty dataframe" in {
      val joinedDataFrame = Seq[DataFrame]().joinDataFramesOnColumns(Seq("basket"))
      assert(joinedDataFrame === spark.emptyDataFrame)
    }
    "when passed an empty list of joinColumns return the dataframes crossjoined" in {
      val sequenceOfDataFrames = Seq[DataFrame](fruitDf,fruitDf, fruitDf)
      val joinedDataFrame = sequenceOfDataFrames.joinDataFramesOnColumns(Seq[String]())
      assert(joinedDataFrame.count === scala.math.pow(fruitDf.count, sequenceOfDataFrames.size))
      assert(joinedDataFrame.columns.size === fruitDf.columns.size * sequenceOfDataFrames.size)
    }
  }
}

このSparkバグが原因でエラーが発生するまで、これはすべて正常に機能していました:https://issues.apache.org/jira/browse/SPARK-25150これは、結合列が同じ名前の場合、特定の条件下でエラーを引き起こす可能性があります。

回避策は、列を別のものとしてエイリアスすることです。そのため、結合列をエイリアスし、結合を実行してから、名前を元に戻すように関数を書き直しました。

  implicit class SequenceOfDataFrames(dataFrames: Seq[DataFrame]){
    def joinDataFramesOnColumns(joinColumns: Seq[String]) : DataFrame = {
      val emptyDataFrame = SparkSession.builder().getOrCreate().emptyDataFrame
      val nonEmptyDataFrames = dataFrames.filter(_ != emptyDataFrame)
      if (nonEmptyDataFrames.isEmpty){
        emptyDataFrame
      }
      else {
        if (joinColumns.isEmpty) {
          return nonEmptyDataFrames.reduce(_.crossJoin(_))
        }

      /*
      The horrible, gnarly, unelegent code below  would ideally exist simply as:

      nonEmptyDataFrames.reduce(_.join(_, joinColumns))

      however that will fail in certain specific circumstances due to a bug in spark,
      see https://issues.apache.org/jira/browse/SPARK-25150 for details
       */
      val aliasSuffix = "_aliased"
      val aliasedJoinColumns = joinColumns.map(joinColumn => joinColumn+aliasSuffix)
      var aliasedNonEmptyDataFrames: Seq[DataFrame] = Seq()
      nonEmptyDataFrames.foreach(
        nonEmptyDataFrame =>{
          var tempNonEmptyDataFrame = nonEmptyDataFrame
          joinColumns.foreach(
            joinColumn => {
              tempNonEmptyDataFrame = tempNonEmptyDataFrame.withColumnRenamed(joinColumn, joinColumn+aliasSuffix)
            }
          )
          aliasedNonEmptyDataFrames = aliasedNonEmptyDataFrames :+ tempNonEmptyDataFrame
        }
      )
      var joinedAliasedNonEmptyDataFrames = aliasedNonEmptyDataFrames.reduce(_.join(_, aliasedJoinColumns))
      joinColumns.foreach(
        joinColumn => joinedAliasedNonEmptyDataFrames = joinedAliasedNonEmptyDataFrames.withColumnRenamed(
          joinColumn+aliasSuffix, joinColumn
        )
      )
      joinedAliasedNonEmptyDataFrames
    }
  }
}

テストはまだ合格しているので、かなり満足していvarますがvar、各反復で結果をそれ戻すループを調べています...特に元のテストと比較して、かなりエレガントではなく、醜いです関数のバージョン。varsを使わなくてもいいように書く方法があるはずなのに、試行錯誤の末、これが一番いいと思います。

誰かがよりエレガントな解決策を提案できますか?初心者のScala開発者として、このような問題を解決する慣用的な方法に慣れることは本当に助けになります。

コードの残りの部分(テストなど)に関する建設的なコメントも歓迎します

ジャミエット

foldLeft()の使用を提案してくれた@Duelistに感謝します。ScalaのfoldLeftはDataFrameでどのように機能しますか?これにより、コードをそのように適合させて、varsを排除することになりました

  implicit class SequenceOfDataFrames(dataFrames: Seq[DataFrame]){
    def joinDataFramesOnColumns(joinColumns: Seq[String]) : DataFrame = {
      val emptyDataFrame = SparkSession.builder().getOrCreate().emptyDataFrame
      val nonEmptyDataFrames = dataFrames.filter(_ != emptyDataFrame)
      if (nonEmptyDataFrames.isEmpty){
        emptyDataFrame
      }
      else {
        if (joinColumns.isEmpty) {
          return nonEmptyDataFrames.reduce(_.crossJoin(_))
        }

        /*
        The code below  would ideally exist simply as:

        nonEmptyDataFrames.reduce(_.join(_, joinColumns))

        however that will fail in certain specific circumstances due to a bug in spark,
        see https://issues.apache.org/jira/browse/SPARK-25150 for details

        hence this code aliases the joinColumns, performs the join, then renames the 
        aliased columns back to their original name
         */
        val aliasSuffix = "_aliased"
        val aliasedJoinColumns = joinColumns.map(joinColumn => joinColumn+aliasSuffix)
        val joinedAliasedNonEmptyDataFrames = nonEmptyDataFrames.foldLeft(Seq[DataFrame]()){
          (tempDf, nonEmptyDataFrame) => tempDf :+ joinColumns.foldLeft(nonEmptyDataFrame){
            (tempDf2, joinColumn) => tempDf2.withColumnRenamed(joinColumn, joinColumn+aliasSuffix)
          }
        }.reduce(_.join(_, aliasedJoinColumns))
        joinColumns.foldLeft(joinedAliasedNonEmptyDataFrames){
          (tempDf, joinColumn) => tempDf.withColumnRenamed(joinColumn+aliasSuffix, joinColumn)
        }
      }
    }
  }

2つのステートメントを1つにまとめて削除することでさらに進めることができましたがval joinedAliasedNonEmptyDataFrames、その暫定的な使用によってもたらされる明確さを好みましたval

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

レガシーコードをリファクタリングするために必要なアドバイス

分類Dev

静的ファクトリーメソッドでオーバーライドされたメソッドでインスタンスを作成するときに、囲んでいるクラスのプライベートフィールドにアクセスするにはどうすればよいですか?

分類Dev

PHPコードを「コンパイル」して、バイトコードインタープリターによって実行されるバイナリ風のファイルをアップロードできますか?

分類Dev

サーバーとクライアントの両方でブロックすることなく、サーバーでリアルタイムに書き込まれているアップロードされたファイルのファイルサイズを読み取ってエコーする方法は?

分類Dev

tornadofxアプリ内のobservableArrayListにバインドされたアイテムのフィルタリングを可能にするためにビューをリファクタリングするにはどうすればよいですか?

分類Dev

反応ネイティブには、アプリが使用されていないとき、またはバックグラウンドモードで実行されている関数ライフサイクルがありますか?

分類Dev

index.htmlからインポートされたJavascriptスクリプトからグローバル変数と関数にアクセスし、それらをコンポーネントで使用するにはどうすればよいですか?

分類Dev

エイリアスとしてバンドルされているGoogleスプレッドシートの数式を使用できますか?

分類Dev

PowerShellを使用してファイルをインポートし、既存のすべてのプレースホルダー変数をスクリプトで定義された変数に置き換えるにはどうすればよいですか?

分類Dev

このスクリプト化されたJenkinsパイプラインを適切にリファクタリングして、共通のコードを1つのファイルに保存するにはどうすればよいですか?

分類Dev

using句内のネストされたtry / catchブロックをリファクタリングする方法に関するアドバイス

分類Dev

アプリスクリプトで「不明」の基準に基づいてフィルタリングすることにより、Googleドライブからファイルを取得するにはどうすればよいですか?

分類Dev

グローバル変数+2つの関数で使用されている変数を削除しますか?

分類Dev

ジェイルブレイクされたiPhoneのSpringBoardまたは他のアプリケーションからアクセスできるグローバル環境変数を作成するにはどうすればよいですか?

分類Dev

ASP.NET MVCバンドルを使用しているときに、クライアントがサーバーにアクセスしてファイルが変更されているかどうかを確認するのはなぜですか?

分類Dev

Mac OS Xのアプリケーションバイナリファイルにドラッグアンドドロップされたファイルのリストを取得するにはどうすればよいですか?

分類Dev

リモートコマンドを使用して、データベースサーバーに保存されているSQLスクリプトファイルを実行することはできますか?

分類Dev

アプリがユーザーによってアクティブに使用されているときに、バックグラウンドスレッドのレイアウトの更新/変更に関する通知を受け取ります

分類Dev

関数型プログラミングスタイルでこのscalaメソッドをリファクタリングするための助けが必要

分類Dev

関数型プログラミングスタイルでこのscalaメソッドをリファクタリングするための助けが必要

分類Dev

再帰的な2dtreeのリファクタリングに関するアドバイスにはメソッドが含まれています

分類Dev

グリフィコンのバグに関するアドバイス?

分類Dev

リバースエンジニアリングされたアプリを使用しているときに、元のSHA1キーを使用して新しいリリースのAPKファイルを作成できますか?

分類Dev

カスタム確認ダイアログを使用して、アップロードされた複数のファイルを dropzone で削除できるようにするにはどうすればよいですか?

分類Dev

ビューのボタンからカスタム メソッドを呼び出して、オブジェクトに関連付けられたファイルを削除するにはどうすればよいですか? Railsアプリ

分類Dev

Bashスクリプトでsedを使用して、ログファイルの「var」変数に含まれる文字列パターンを削除したい

分類Dev

クエリを使用してCSVファイルの値をデータベースにバインドするにはどうすればよいですか?

分類Dev

ReactJS-リファクタリング後に別のファイルから値をロードする関数を取得できません

分類Dev

macOS用のCocoaアプリケーションで、NSFileWrapperを使用してバンドルをディスクに保存します。ファインダーにバンドルの正しいアイコンを表示させるにはどうすればよいですか?

Related 関連記事

  1. 1

    レガシーコードをリファクタリングするために必要なアドバイス

  2. 2

    静的ファクトリーメソッドでオーバーライドされたメソッドでインスタンスを作成するときに、囲んでいるクラスのプライベートフィールドにアクセスするにはどうすればよいですか?

  3. 3

    PHPコードを「コンパイル」して、バイトコードインタープリターによって実行されるバイナリ風のファイルをアップロードできますか?

  4. 4

    サーバーとクライアントの両方でブロックすることなく、サーバーでリアルタイムに書き込まれているアップロードされたファイルのファイルサイズを読み取ってエコーする方法は?

  5. 5

    tornadofxアプリ内のobservableArrayListにバインドされたアイテムのフィルタリングを可能にするためにビューをリファクタリングするにはどうすればよいですか?

  6. 6

    反応ネイティブには、アプリが使用されていないとき、またはバックグラウンドモードで実行されている関数ライフサイクルがありますか?

  7. 7

    index.htmlからインポートされたJavascriptスクリプトからグローバル変数と関数にアクセスし、それらをコンポーネントで使用するにはどうすればよいですか?

  8. 8

    エイリアスとしてバンドルされているGoogleスプレッドシートの数式を使用できますか?

  9. 9

    PowerShellを使用してファイルをインポートし、既存のすべてのプレースホルダー変数をスクリプトで定義された変数に置き換えるにはどうすればよいですか?

  10. 10

    このスクリプト化されたJenkinsパイプラインを適切にリファクタリングして、共通のコードを1つのファイルに保存するにはどうすればよいですか?

  11. 11

    using句内のネストされたtry / catchブロックをリファクタリングする方法に関するアドバイス

  12. 12

    アプリスクリプトで「不明」の基準に基づいてフィルタリングすることにより、Googleドライブからファイルを取得するにはどうすればよいですか?

  13. 13

    グローバル変数+2つの関数で使用されている変数を削除しますか?

  14. 14

    ジェイルブレイクされたiPhoneのSpringBoardまたは他のアプリケーションからアクセスできるグローバル環境変数を作成するにはどうすればよいですか?

  15. 15

    ASP.NET MVCバンドルを使用しているときに、クライアントがサーバーにアクセスしてファイルが変更されているかどうかを確認するのはなぜですか?

  16. 16

    Mac OS Xのアプリケーションバイナリファイルにドラッグアンドドロップされたファイルのリストを取得するにはどうすればよいですか?

  17. 17

    リモートコマンドを使用して、データベースサーバーに保存されているSQLスクリプトファイルを実行することはできますか?

  18. 18

    アプリがユーザーによってアクティブに使用されているときに、バックグラウンドスレッドのレイアウトの更新/変更に関する通知を受け取ります

  19. 19

    関数型プログラミングスタイルでこのscalaメソッドをリファクタリングするための助けが必要

  20. 20

    関数型プログラミングスタイルでこのscalaメソッドをリファクタリングするための助けが必要

  21. 21

    再帰的な2dtreeのリファクタリングに関するアドバイスにはメソッドが含まれています

  22. 22

    グリフィコンのバグに関するアドバイス?

  23. 23

    リバースエンジニアリングされたアプリを使用しているときに、元のSHA1キーを使用して新しいリリースのAPKファイルを作成できますか?

  24. 24

    カスタム確認ダイアログを使用して、アップロードされた複数のファイルを dropzone で削除できるようにするにはどうすればよいですか?

  25. 25

    ビューのボタンからカスタム メソッドを呼び出して、オブジェクトに関連付けられたファイルを削除するにはどうすればよいですか? Railsアプリ

  26. 26

    Bashスクリプトでsedを使用して、ログファイルの「var」変数に含まれる文字列パターンを削除したい

  27. 27

    クエリを使用してCSVファイルの値をデータベースにバインドするにはどうすればよいですか?

  28. 28

    ReactJS-リファクタリング後に別のファイルから値をロードする関数を取得できません

  29. 29

    macOS用のCocoaアプリケーションで、NSFileWrapperを使用してバンドルをディスクに保存します。ファインダーにバンドルの正しいアイコンを表示させるにはどうすればよいですか?

ホットタグ

アーカイブ