фильтр spark dataframe с полем строки, представляющим собой массив строк
использование Spark 1.5 и Scala 2.10.6
Я пытаюсь фильтровать фрейм данных через поле "теги", которое представляет собой массив строк. Поиск всех строк, которые имеют тег "private".
val report = df.select("*")
.where(df("tags").contains("private"))
начало:
исключение в потоке" main " org.апаш.искра.язык SQL.AnalysisException: не удается разрешить "содержит (теги, частные)" из-за несоответствия типов данных: аргумент 1 требует строкового типа, однако "теги" имеют массив тип.;
Is метод фильтра лучше подходит?
обновление:
данные поступают от адаптера cassandra, но минимальный пример, который показывает, что я пытаюсь сделать, а также получает вышеуказанную ошибку:
def testData (sc: SparkContext): DataFrame = {
val stringRDD = sc.parallelize(Seq("""
{ "name": "ed",
"tags": ["red", "private"]
}""",
"""{ "name": "fred",
"tags": ["public", "blue"]
}""")
)
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
import sqlContext.implicits._
sqlContext.read.json(stringRDD)
}
def run(sc: SparkContext) {
val df1 = testData(sc)
df1.show()
val report = df1.select("*")
.where(df1("tags").contains("private"))
report.show()
}
обновлено: массив тегов может быть любой длины, а "частный" тег может быть в любом положении
обновлено: одно решение, которое работает: UDF
val filterPriv = udf {(tags: mutable.WrappedArray[String]) => tags.contains("private")}
val report = df1.filter(filterPriv(df1("tags")))
2 ответов
Я думаю, если вы используете where(array_contains(...))
он будет работать. Вот мой результат:
scala> import org.apache.spark.SparkContext
import org.apache.spark.SparkContext
scala> import org.apache.spark.sql.DataFrame
import org.apache.spark.sql.DataFrame
scala> def testData (sc: SparkContext): DataFrame = {
| val stringRDD = sc.parallelize(Seq
| ("""{ "name": "ned", "tags": ["blue", "big", "private"] }""",
| """{ "name": "albert", "tags": ["private", "lumpy"] }""",
| """{ "name": "zed", "tags": ["big", "private", "square"] }""",
| """{ "name": "jed", "tags": ["green", "small", "round"] }""",
| """{ "name": "ed", "tags": ["red", "private"] }""",
| """{ "name": "fred", "tags": ["public", "blue"] }"""))
| val sqlContext = new org.apache.spark.sql.SQLContext(sc)
| import sqlContext.implicits._
| sqlContext.read.json(stringRDD)
| }
testData: (sc: org.apache.spark.SparkContext)org.apache.spark.sql.DataFrame
scala>
| val df = testData (sc)
df: org.apache.spark.sql.DataFrame = [name: string, tags: array<string>]
scala> val report = df.select ("*").where (array_contains (df("tags"), "private"))
report: org.apache.spark.sql.DataFrame = [name: string, tags: array<string>]
scala> report.show
+------+--------------------+
| name| tags|
+------+--------------------+
| ned|[blue, big, private]|
|albert| [private, lumpy]|
| zed|[big, private, sq...|
| ed| [red, private]|
+------+--------------------+
обратите внимание, что он работает, если вы пишете where(array_contains(df("tags"), "private"))
, но если вы пишете where(df("tags").array_contains("private"))
(более прямо аналогично тому, что вы написали изначально) он терпит неудачу с array_contains is not a member of org.apache.spark.sql.Column
. Глядя на исходный код Column
, Я вижу, что есть некоторые вещи, чтобы обрабатывать contains
(строительства Contains
экземпляр для этого), но не array_contains
. Может, это недосмотр.
вы можете использовать порядковый номер для ссылки на массив json, например, в вашем случае df("tags")(0)
. Вот рабочий образец
scala> val stringRDD = sc.parallelize(Seq("""
| { "name": "ed",
| "tags": ["private"]
| }""",
| """{ "name": "fred",
| "tags": ["public"]
| }""")
| )
stringRDD: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[87] at parallelize at <console>:22
scala> import sqlContext.implicits._
import sqlContext.implicits._
scala> sqlContext.read.json(stringRDD)
res28: org.apache.spark.sql.DataFrame = [name: string, tags: array<string>]
scala> val df=sqlContext.read.json(stringRDD)
df: org.apache.spark.sql.DataFrame = [name: string, tags: array<string>]
scala> df.columns
res29: Array[String] = Array(name, tags)
scala> df.dtypes
res30: Array[(String, String)] = Array((name,StringType), (tags,ArrayType(StringType,true)))
scala> val report = df.select("*").where(df("tags")(0).contains("private"))
report: org.apache.spark.sql.DataFrame = [name: string, tags: array<string>]
scala> report.show
+----+-------------+
|name| tags|
+----+-------------+
| ed|List(private)|
+----+-------------+