動的に作成される次の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) }
}
row
はRow
型ですが、反復可能ではないため、このコードはrow.foreach
でコンパイルエラーをスローします。 Row
の各列を繰り返す方法
Row
を使用して、Seq
をtoSeq
に変換できます。 Seq
になったら、通常どおりforeach
、map
、または必要なものを使用して繰り返し処理できます。
sqlDF.foreach { row =>
row.toSeq.foreach{col => println(col) }
}
出力:
Berta
bbb
30
Joe
Andy
aaa
20
ccc
40
以下のような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}"))
以下の結果をご覧ください。
アプローチ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.collect
はaction
で、すべてのデータをドライバーのメモリに返します。ドライバーのメモリは、データを保持するのにそれほど大きくない場合があり、最終的にアプリケーションが失敗します。
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("]","")
sqlDF.foreach
は機能していませんが、@ Sarath Avanavuの回答1のアプローチ1は動作しますが、レコードの順序で遊んでいました。
私は働いているもう1つの方法を見つけました
df.collect().foreach { row =>
println(row.mkString(","))
}
単純な結果の収集とforeachの適用
df.collect().foreach(println)
mkString
でRow
を使用する必要があります。
sqlDF.foreach { row =>
println(row.mkString(","))
}
ただし、これはexecutor JVMの内部で出力されるため、通常は出力は表示されません(master = localで作業している場合を除く)