apache spark - Spark DataFrame: cuente valores distintos de cada columna
apache-spark apache-spark-sql (5)
Agregando a la respuesta de desaiankitb, esto le proporcionaría una respuesta más intuitiva:
del
pyspark.sql.functions
importación de
pyspark.sql.functions
df.groupBy(colname).count().show()
La pregunta está más o menos en el título: ¿Hay una manera eficiente de contar los valores distintos en cada columna en un DataFrame?
El método describe proporciona solo el recuento, pero no el recuento distinto, y me pregunto si hay una manera de obtener el recuento distinto para todas las columnas (o algunas seleccionadas).
En
pySpark
podría hacer algo como esto, usando
countDistinct()
:
from pyspark.sql.functions import col, countDistinct
df.agg(*(countDistinct(col(c)).alias(c) for c in df.columns))
De manera similar en
Scala
:
import org.apache.spark.sql.functions.countDistinct
import org.apache.spark.sql.functions.col
df.select(df.columns.map(c => countDistinct(col(c)).alias(c)): _*)
Si desea acelerar las cosas ante la posible pérdida de precisión, también puede usar
approxCountDistinct()
.
Las agregaciones múltiples serían bastante caras de calcular. Sugiero que utilice métodos de aproximación en su lugar. En este caso, recuento distinto aproximado:
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|
// +---------------------------+---------------------------+---------------------------+
El método
approx_count_distinct
se basa en
HyperLogLog
debajo del capó.
El algoritmo HyperLogLog y su variante HyperLogLog ++ (implementado en Spark) se basa en la siguiente observación inteligente .
Si los números se distribuyen uniformemente en un rango, entonces el recuento de elementos distintos se puede aproximar a partir del mayor número de ceros iniciales en la representación binaria de los números.
Por ejemplo, si observamos un número cuyos dígitos en forma binaria son de la forma
0…(k times)…01…1
, entonces podemos estimar que hay en el orden de 2 ^ k elementos en el conjunto.
Esta es una estimación muy cruda, pero se puede refinar con gran precisión con un algoritmo de dibujo.
Se puede encontrar una explicación detallada de la mecánica detrás de este algoritmo en el documento original .
Nota: Al
iniciar
Spark 1.6
, cuando Spark llama a
SELECT SOME_AGG(DISTINCT foo)), SOME_AGG(DISTINCT bar)) FROM df
cada cláusula debe activar una agregación separada para cada cláusula.
Mientras que esto es diferente de
SELECT SOME_AGG(foo), SOME_AGG(bar) FROM df
donde
SELECT SOME_AGG(foo), SOME_AGG(bar) FROM df
una vez.
Por lo tanto, el rendimiento no será comparable cuando se utiliza un
count(distinct(_))
y
approxCountDistinct
(o
approx_count_distinct
).
Es uno de los cambios de comportamiento desde Spark 1.6 :
Con el planificador de consultas mejorado para consultas que tienen agregaciones distintas (SPARK-9241), el plan de una consulta que tiene una única agregación distinta se ha cambiado a una versión más robusta. Para volver al plan generado por el planificador de Spark 1.5, establezca spark.sql.specializeSingleDistinctAggPlanning en verdadero. (SPARK-12077)
Referencia: Algoritmos aproximados en Apache Spark: HyperLogLog y Quantiles .
Puede usar la función de
count(column name)
de SQL
Alternativamente, si está utilizando el análisis de datos y desea una estimación aproximada y no un recuento exacto de todas y cada una de las columnas, puede usar la función
approx_count_distinct(expr[, relativeSD])
Si solo desea contar para una columna en particular, lo siguiente podría ayudar.
Aunque es una respuesta tardía.
Puede ayudar a alguien.
(
pyspark 2.2.0
probado)
from pyspark.sql.functions import col, countDistinct
df.agg(countDistinct(col("colName")).alias("count")).show()