Ответ 1
Многочисленные агрегации были бы довольно дорогими для вычисления. Я предлагаю вместо этого использовать методы приближения. В этом случае приблизительный отчетливый счет:
val df = Seq((1,3,4),(1,2,3),(2,3,4),(2,3,5)).toDF("col1","col2","col3")
val exprs = df.columns.map((_ -> "approx_count_distinct")).toMap
df.agg(exprs).show()
// +---------------------------+---------------------------+---------------------------+
// |approx_count_distinct(col1)|approx_count_distinct(col2)|approx_count_distinct(col3)|
// +---------------------------+---------------------------+---------------------------+
// | 2| 2| 3|
// +---------------------------+---------------------------+---------------------------+
Метод approx_count_distinct
опирается на HyperLogLog под капотом.
Алгоритм HyperLogLog и его вариант HyperLogLog++ (реализованный в Spark) опирается на следующее умное наблюдение.
Если числа равномерно распределены по диапазону, то число отдельных элементов может быть аппроксимировано по наибольшему числу ведущих нулей в двоичном представлении чисел.
Например, если мы наблюдаем число, чьи цифры в двоичной форме имеют вид 0…(k times)…01…1
, то мы можем оценить, что в наборе есть порядка 2 ^ k элементов. Это очень грубая оценка, но ее можно уточнить с высокой точностью с помощью алгоритма зарисовки.
Подробное объяснение механизма, лежащего в основе этого алгоритма, можно найти в оригинальной статье.
Примечание. Начиная с Spark 1.6, когда Spark вызывает SELECT SOME_AGG(DISTINCT foo)), SOME_AGG(DISTINCT bar)) FROM df
каждого предложения должно запускаться отдельное агрегирование для каждого предложения. Тогда как это отличается от SELECT SOME_AGG(foo), SOME_AGG(bar) FROM df
где мы агрегируем один раз. Таким образом, производительность не будет сравнимой при использовании count(distinct(_))
и approxCountDistinct
(или approx_count_distinct
).
Это одно из изменений поведения со времен Spark 1.6:
С улучшенным планировщиком запросов для запросов, имеющих различные агрегации (SPARK-9241), план запроса, имеющий одну отдельную агрегацию, был изменен на более надежную версию. Чтобы вернуться к плану, сгенерированному планировщиком Spark 1.5s, установите для параметра spark.sql.specializeSingleDistinctAggPlanning значение true. (СПАРК-12077)
Ссылка: Приближенные алгоритмы в Apache Spark: HyperLogLog и Quantiles.