Te recomendamos que revises esta respuesta en un entorno controlado antes de pasarlo a producción, saludos.
Solución:
Una forma de hacerlo es mediante el uso de un pyspark.sql.Window
para agregar una columna que cuente el número de duplicados para cada fila ("ID", "ID2", "Name")
combinación. Luego seleccione solo las filas donde el número de duplicados es mayor que 1.
import pyspark.sql.functions as f
from pyspark.sql import Window
w = Window.partitionBy('ID', 'ID2', 'Number')
df.select('*', f.count('ID').over(w).alias('dupeCount'))
.where('dupeCount > 1')
.drop('dupeCount')
.show()
#+---+---+------+----+------------+------------+
#| ID|ID2|Number|Name|Opening_Hour|Closing_Hour|
#+---+---+------+----+------------+------------+
#|ALT|QWA| 2|null| 08:54:00| 23:25:00|
#|ALT|QWA| 2|null| 08:53:00| 23:24:00|
#|ALT|QWA| 6|null| 08:59:00| 23:30:00|
#|ALT|QWA| 6|null| 08:55:00| 23:26:00|
#+---+---+------+----+------------+------------+
solía pyspark.sql.functions.count()
para contar el número de artículos en cada grupo. Esto devuelve un DataFrame que contiene todos los duplicados (el segundo resultado que mostró).
Si desea obtener solo una fila por ("ID", "ID2", "Name")
combinación, podría hacerlo usando otra ventana para ordenar las filas.
Por ejemplo, a continuación agrego otra columna para el row_number
y seleccione solo las filas donde el recuento de duplicados sea mayor que 1 y el número de fila sea igual a 1. Esto garantiza una fila por agrupación.
w2 = Window.partitionBy('ID', 'ID2', 'Number').orderBy('ID', 'ID2', 'Number')
df.select(
'*',
f.count('ID').over(w).alias('dupeCount'),
f.row_number().over(w2).alias('rowNum')
)
.where('(dupeCount > 1) AND (rowNum = 1)')
.drop('dupeCount', 'rowNum')
.show()
#+---+---+------+----+------------+------------+
#| ID|ID2|Number|Name|Opening_Hour|Closing_Hour|
#+---+---+------+----+------------+------------+
#|ALT|QWA| 2|null| 08:54:00| 23:25:00|
#|ALT|QWA| 6|null| 08:59:00| 23:30:00|
#+---+---+------+----+------------+------------+
Aquí hay una manera de hacerlo sin Window.
Un DataFrame con los duplicados
df.exceptAll(df.drop_duplicates(['ID', 'ID2', 'Number'])).show()
# +---+---+------+------------+------------+
# | ID|ID2|Number|Opening_Hour|Closing_Hour|
# +---+---+------+------------+------------+
# |ALT|QWA| 2| 08:53:00| 23:24:00|
# |ALT|QWA| 6| 08:55:00| 23:26:00|
# +---+---+------+------------+------------+
Un DataFrame con todos los duplicados (usando la combinación left_anti)
df.join(df.groupBy('ID', 'ID2', 'Number')
.count().where('count = 1').drop('count'),
on=['ID', 'ID2', 'Number'],
how='left_anti').show()
# +---+---+------+------------+------------+
# | ID|ID2|Number|Opening_Hour|Closing_Hour|
# +---+---+------+------------+------------+
# |ALT|QWA| 2| 08:54:00| 23:25:00|
# |ALT|QWA| 2| 08:53:00| 23:24:00|
# |ALT|QWA| 6| 08:59:00| 23:30:00|
# |ALT|QWA| 6| 08:55:00| 23:26:00|
# +---+---+------+------------+------------+
Para ampliar la respuesta realmente excelente de Paul: a menudo necesito subdividir un marco de datos en solo entradas que se repiten x veces, y dado que necesito hacer esto con mucha frecuencia, convertí esto en una función que solo importo con muchas otras funciones auxiliares al principio de mis guiones:
import pyspark.sql.functions as f
from pyspark.sql import Window
def get_entries_with_frequency(df, cols, num):
if type(cols)==str:
cols = [cols]
w = Window.partitionBy(cols)
return df.select('*', f.count(cols[0]).over(w).alias('dupeCount'))
.where("dupeCount = ".format(num))
.drop('dupeCount')
Aquí tienes las reseñas y valoraciones
No se te olvide dar difusión a este ensayo si te fue de ayuda.