web-dev-qa-db-ja.com

Sparkデータフレームの行と列を繰り返します

動的に作成される次のSparkデータフレームがあります。

val sf1 = StructField("name", StringType, nullable = true)
val sf2 = StructField("sector", StringType, nullable = true)
val sf3 = StructField("age", IntegerType, nullable = true)

val fields = List(sf1,sf2,sf3)
val schema = StructType(fields)

val row1 = Row("Andy","aaa",20)
val row2 = Row("Berta","bbb",30)
val row3 = Row("Joe","ccc",40)

val data = Seq(row1,row2,row3)

val df = spark.createDataFrame(spark.sparkContext.parallelize(data), schema)

df.createOrReplaceTempView("people")
val sqlDF = spark.sql("SELECT * FROM people")

ここで、各列を印刷するためにsqlDFの各行と列を繰り返す必要があります。これは私の試みです。

sqlDF.foreach { row =>
  row.foreach { col => println(col) }
}

rowRow型ですが、反復可能ではないため、このコードはrow.foreachでコンパイルエラーをスローします。 Rowの各列を繰り返す方法

16
ps0604

Rowを使用して、SeqtoSeqに変換できます。 Seqになったら、通常どおりforeachmap、または必要なものを使用して繰り返し処理できます。

    sqlDF.foreach { row => 
           row.toSeq.foreach{col => println(col) }
    }

出力:

Berta
bbb
30
Joe
Andy
aaa
20
ccc
40
10
SCouto

以下のようなDataframeがあると考えてください

+-----+------+---+
| name|sector|age|
+-----+------+---+
| Andy|   aaa| 20|
|Berta|   bbb| 30|
|  Joe|   ccc| 40|
+-----+------+---+

Dataframeをループし、Dataframeから要素を抽出するには、次のことができます。以下のいずれかのアプローチを選択しました。

アプローチ1-foreachを使用したループ

foreachループを使用してデータフレームを直接ループすることはできません。これを行うには、最初にcase classを使用してデータフレームのスキーマを定義し、次にこのスキーマをデータフレームに指定する必要があります。

import spark.implicits._
import org.Apache.spark.sql._
case class cls_Employee(name:String, sector:String, age:Int)
val df = Seq(cls_Employee("Andy","aaa", 20), cls_Employee("Berta","bbb", 30), cls_Employee("Joe","ccc", 40)).toDF()
df.as[cls_Employee].take(df.count.toInt).foreach(t => println(s"name=${t.name},sector=${t.sector},age=${t.age}"))

以下の結果をご覧ください。

enter image description here

アプローチ2-rddを使用したループ

Dataframeの上にrdd.collectを使用します。 row変数には、rdd行タイプのDataframeの各行が含まれます。行から各要素を取得するには、row.mkString(",")を使用します。これには、各行の値がコンマ区切り値で含まれます。 split関数(組み込み関数)を使用すると、rdd行の各列値にインデックスを使用してアクセスできます。

for (row <- df.rdd.collect)
{   
    var name = row.mkString(",").split(",")(0)
    var sector = row.mkString(",").split(",")(1)
    var age = row.mkString(",").split(",")(2)   
}

このアプローチには2つの欠点があることに注意してください。
1。列の値に,がある場合、データは誤って隣接する列に分割されます。
2。 rdd.collectactionで、すべてのデータをドライバーのメモリに返します。ドライバーのメモリは、データを保持するのにそれほど大きくない場合があり、最終的にアプリケーションが失敗します。

Approach 1を使用することをお勧めします。

アプローチ3-whereとselectを使用して

内部でループしてデータを見つけるwhereおよびselectを直接使用できます。 Indexを範囲外の例外としてスローするべきではないため、if条件が使用されます

if(df.where($"name" === "Andy").select(col("name")).collect().length >= 1)
    name = df.where($"name" === "Andy").select(col("name")).collect()(0).get(0).toString

アプローチ4-一時テーブルの使用

スパークのメモリに保存されるtemptableとしてデータフレームを登録できます。その後、他のデータベースと同様に選択クエリを使用してデータをクエリし、変数を収集して保存できます

df.registerTempTable("student")
name = sqlContext.sql("select name from student where name='Andy'").collect()(0).toString().replace("[","").replace("]","")
12
Sarath Avanavu

sqlDF.foreachは機能していませんが、@ Sarath Avanavuの回答1のアプローチ1は動作しますが、レコードの順序で遊んでいました。

私は働いているもう1つの方法を見つけました

df.collect().foreach { row =>
   println(row.mkString(","))
}
1
Naresh Joshi

単純な結果の収集とforeachの適用

df.collect().foreach(println)

1
GANESH CHOKHARE

mkStringRowを使用する必要があります。

sqlDF.foreach { row =>
  println(row.mkString(",")) 
}

ただし、これはexecutor JVMの内部で出力されるため、通常は出力は表示されません(master = localで作業している場合を除く)

1
Raphael Roth