Фильтр фрейма данных на основе имен столбцов и их содержимого

#scala #dataframe #apache-spark #apache-spark-sql

Вопрос:

У меня есть этот фрейм данных :

  ----- ---------- --------- 
|num  |Timestamp |frequency|
 ----- ---------- --------- 
|20.0 |1632899456|4        |
|20.0 |1632901256|4        |
|20.0 |1632901796|4        |
|20.0 |1632899155|4        |
|10.0 |1632901743|2        |
|10.0 |1632899933|2        |
|91.0 |1632899756|3        |
|32.0 |1632900776|6        |
|41.0 |1632900176|1        |
 ----- ---------- --------- 
 

Я хочу отфильтровать его, используя содержимое этой карты :

 val filter : Map[String, Seq[String]] = Map("num"-> Seq("20.0", "41.0"), "frequency" -> Seq("6"))
 

Новый фрейм данных будет выглядеть следующим образом :

  ----- ---------- --------- 
|num  |Timestamp |frequency|
 ----- ---------- --------- 
|20.0 |1632899456|4        |
|20.0 |1632901256|4        |
|20.0 |1632901796|4        |
|20.0 |1632899155|4        |
|32.0 |1632900776|6        |
|41.0 |1632900176|1        |
 ----- ---------- --------- 
 

Я использую Spark версии 3.0.2 и SQLContext с языком scala.

Комментарии:

1. Ваш вопрос не ясен. Я думаю, вы хотите отфильтровать записи по предикатам (число 20,0 или 41,0) или (частота 6), верно?

2. Да, это будут предикаты : (число равно 20,0 или 41,0) или (частота равна 6)

3. Если ваши предикаты исправлены, очень прямой путь df.filter($ "num" === "20.0" || $"num" === "41.0" || $"frequency" === "6")

Ответ №1:

Вы можете использовать фильтр с isin функцией

 val filter: Map[String, Seq[String]] = Map("num" -> Seq("20.0", "41.0"), "frequency" -> Seq("6"))

df.filter(($"num" isin (filter("num"):_*)) or
  ($"frequency" isin (filter("frequency"):_*)))
  .show(false)
 

Если у вас есть карта, которая соответствует названию столбца, вы можете использовать

 val filterQuery = filter.map(rec => col(rec._1) isin(rec._2:_*)).reduce(_ or _)

df.filter(filterQuery).show(false)
 

Выход:

  ---- ---------- --------- 
|num |Timestamp |frequency|
 ---- ---------- --------- 
|20.0|1632899456|4        |
|20.0|1632901256|4        |
|20.0|1632901796|4        |
|20.0|1632899155|4        |
|32.0|1632900776|6        |
|41.0|1632900176|1        |
 ---- ---------- --------- 
 

Комментарии:

1. Большое спасибо за ваш ответ. Есть ли какой-нибудь способ перебирать ключи карты вместо того, чтобы вызывать их по одному ?

2. если имя столбца и ключ карты совпадают, вы можете выполнить итерацию

Ответ №2:

Вы можете построить строковый запрос с картой в качестве источника следующим образом:

 val filterQuery = filter
                   .map{case (k,v) => v.map(v1 => (k,v1))}
                   .flatten
                   .map{ case (k,v) => s"$k = $v" }
                   .mkString(" or ")

df.filter(filterQuery)
 

Однако это не элегантно