与えられたデータフレームdf
id | date
---------------
1 | 2015-09-01
2 | 2015-09-01
1 | 2015-09-03
1 | 2015-09-04
2 | 2015-09-04
実行中のカウンターまたはインデックスを作成したいのですが、
したがって、
id | date | counter
--------------------------
1 | 2015-09-01 | 1
1 | 2015-09-03 | 2
1 | 2015-09-04 | 3
2 | 2015-09-01 | 1
2 | 2015-09-04 | 2
これは、ウィンドウ関数で実現できるものです。
val w = Window.partitionBy("id").orderBy("date")
val resultDF = df.select( df("id"), rowNumber().over(w) )
残念ながら、Spark 1.4.1は、通常のデータフレームのウィンドウ関数をサポートしていません。
org.Apache.spark.sql.AnalysisException: Could not resolve window function 'row_number'. Note that, using window functions currently requires a HiveContext;
ありがとう!
これはRDDで行うことができます。個人的には、RDD用のAPIの方がはるかに理にかなっていると思います。データをデータフレームのように「フラット」にする必要はありません。
val df = sqlContext.sql("select 1, '2015-09-01'"
).unionAll(sqlContext.sql("select 2, '2015-09-01'")
).unionAll(sqlContext.sql("select 1, '2015-09-03'")
).unionAll(sqlContext.sql("select 1, '2015-09-04'")
).unionAll(sqlContext.sql("select 2, '2015-09-04'"))
// dataframe as an RDD (of Row objects)
df.rdd
// grouping by the first column of the row
.groupBy(r => r(0))
// map each group - an Iterable[Row] - to a list and sort by the second column
.map(g => g._2.toList.sortBy(row => row(1).toString))
.collect()
上記の結果は次のようになります。
Array[List[org.Apache.spark.sql.Row]] =
Array(
List([1,2015-09-01], [1,2015-09-03], [1,2015-09-04]),
List([2,2015-09-01], [2,2015-09-04]))
'group'内の位置も必要な場合は、zipWithIndex
を使用できます。
df.rdd.groupBy(r => r(0)).map(g =>
g._2.toList.sortBy(row => row(1).toString).zipWithIndex).collect()
Array[List[(org.Apache.spark.sql.Row, Int)]] = Array(
List(([1,2015-09-01],0), ([1,2015-09-03],1), ([1,2015-09-04],2)),
List(([2,2015-09-01],0), ([2,2015-09-04],1)))
could FlatMapを使用してこれをRow
オブジェクトの単純なリスト/配列にフラット化しますが、「グループ」で何かを実行する必要がある場合は、それは良い考えではありません。
このようにRDDを使用することの欠点は、DataFrameからRDDに変換してから元に戻すのが面倒なことです。
ローカルのHiveContext
にもDataFrames
を使用できます。そうしないという非常に正当な理由がない限り、とにかくそれはおそらく良い考えです。これは、spark-Shell
およびSQLContext
シェルで使用可能なデフォルトのpyspark
であり(現時点では、sparkR
はプレーンなSQLContext
を使用しているようです)、そのパーサーは Spark SQLおよびDataFrameガイド が推奨します。
import org.Apache.spark.{SparkContext, SparkConf}
import org.Apache.spark.sql.Hive.HiveContext
import org.Apache.spark.sql.expressions.Window
import org.Apache.spark.sql.functions.rowNumber
object HiveContextTest {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("Hive Context")
val sc = new SparkContext(conf)
val sqlContext = new HiveContext(sc)
import sqlContext.implicits._
val df = sc.parallelize(
("foo", 1) :: ("foo", 2) :: ("bar", 1) :: ("bar", 2) :: Nil
).toDF("k", "v")
val w = Window.partitionBy($"k").orderBy($"v")
df.select($"k", $"v", rowNumber.over(w).alias("rn")).show
}
}
Sparkバージョン(> =)1.5がある場合は、DataFrameのウィンドウ関数が最適です。ただし、実際に古いバージョン(1.4.1など)で立ち往生している場合は、これを解決するためのハッキーな方法は次のとおりです
val df = sc.parallelize((1, "2015-09-01") :: (2, "2015-09-01") :: (1, "2015-09-03") :: (1, "2015-09-04") :: (1, "2015-09-04") :: Nil)
.toDF("id", "date")
val dfDuplicate = df.selecExpr("id as idDup", "date as dateDup")
val dfWithCounter = df.join(dfDuplicate,$"id"===$"idDup")
.where($"date"<=$"dateDup")
.groupBy($"id", $"date")
.agg($"id", $"date", count($"idDup").as("counter"))
.select($"id",$"date",$"counter")
dfWithCounter.show
を実行すると
あなたが得るでしょう:
+---+----------+-------+
| id| date|counter|
+---+----------+-------+
| 1|2015-09-01| 1|
| 1|2015-09-04| 3|
| 1|2015-09-03| 2|
| 2|2015-09-01| 1|
| 2|2015-09-04| 2|
+---+----------+-------+
date
はソートされていませんが、counter
は正しいことに注意してください。また、counter
ステートメントで<=
を>=
に変更することにより、where
の順序を変更することもできます。