[Minería de datos] ¿Cómo asegurar la consistencia de los datos?

 

1. Descripción

         Solía ​​trabajar como analista de datos para una empresa de servicios de análisis web. Dichos sistemas ayudan a los sitios web a recopilar y analizar datos de comportamiento de los clientes. No hace falta decir que los datos son el valor más valioso de los servicios de análisis web. Uno de mis objetivos principales es monitorear la calidad de los datos.

Para asegurarnos de que todo esté bien con los datos, debemos centrarnos en dos cosas:

  • No hay eventos faltantes ni duplicados -> los recuentos de eventos y sesiones están dentro del rango esperado.
  • Los datos son correctos -> la distribución de valores para cada parámetro sigue siendo la misma, la otra versión aún tiene que comenzar a registrar todos los navegadores como Safari o dejar de rastrear las compras por completo.

        Hoy quiero contarles mi experiencia con esta compleja tarea. Como beneficio adicional, mostraré ejemplos de funciones de matriz de ClickHouse.

Foto de Luke Chesser  en  Unsplash

2. ¿Qué es el análisis de redes?

        Los sistemas de análisis web registran una gran cantidad de información sobre eventos en un sitio web, por ejemplo, qué navegadores y sistemas operativos utilizan los clientes, qué URL visitan, cuánto tiempo pasan en el sitio web e incluso qué productos agregan a sus compras. carros y compra. Todos estos datos se pueden usar para informes (para comprender cuántos clientes visitaron el sitio) o análisis (para comprender los puntos débiles y mejorar la experiencia del cliente). Puede encontrar más detalles sobre análisis web en Wikipedia .

        Usaremos los datos anónimos de análisis web de ClickHouse. Puede encontrar una guía que describe cómo cargarlo aquí .

        Veamos los datos. es el identificador único de la sesión, mientras que los demás parámetros son características de esta sesión. Parecen variables numéricas, pero son nombres codificados del navegador y del sistema operativo. Es mucho más eficiente almacenar estos valores (como números) y luego decodificar los valores a nivel de aplicación. Esta optimización es muy importante y puede ahorrarle terabytes si se trata de big data.VisitIDUserAgentOS

SELECT
    VisitID,
    StartDate,
    UTCStartTime,
    Duration,
    PageViews,
    StartURLDomain,
    IsMobile,
    UserAgent,
    OS
FROM datasets.visits_v1
FINAL
LIMIT 10

┌─────────────VisitID─┬──StartDate─┬────────UTCStartTime─┬─Duration─┬─PageViews─┬─StartURLDomain─────────┬─IsMobile─┬─UserAgent─┬──OS─┐
│ 6949594573706600954 │ 2014-03-17 │ 2014-03-17 11:38:42 │        0 │         1 │ gruzomoy.sumtel.com.ua │        0 │         7 │   2 │
│ 7763399689682887827 │ 2014-03-17 │ 2014-03-17 18:22:20 │       24 │         3 │ gruzomoy.sumtel.com.ua │        0 │         2 │   2 │
│ 9153706821504089082 │ 2014-03-17 │ 2014-03-17 09:41:09 │      415 │         9 │ gruzomoy.sumtel.com.ua │        0 │         7 │  35 │
│ 5747643029332244007 │ 2014-03-17 │ 2014-03-17 04:46:08 │       19 │         1 │ gruzomoy.sumtel.com.ua │        0 │         2 │ 238 │
│ 5868920473837897470 │ 2014-03-17 │ 2014-03-17 10:10:31 │       11 │         1 │ gruzomoy.sumtel.com.ua │        0 │         3 │  35 │
│ 6587050697748196290 │ 2014-03-17 │ 2014-03-17 09:06:47 │       18 │         2 │ gruzomoy.sumtel.com.ua │        0 │       120 │  35 │
│ 8872348705743297525 │ 2014-03-17 │ 2014-03-17 06:40:43 │      190 │         6 │ gruzomoy.sumtel.com.ua │        0 │         5 │ 238 │
│ 8890846394730359529 │ 2014-03-17 │ 2014-03-17 02:27:19 │        0 │         1 │ gruzomoy.sumtel.com.ua │        0 │        57 │  35 │
│ 7429587367586011403 │ 2014-03-17 │ 2014-03-17 01:13:14 │        0 │         1 │ gruzomoy.sumtel.com.ua │        1 │         1 │  12 │
│ 5195928066127503662 │ 2014-03-17 │ 2014-03-17 01:43:02 │     1926 │         3 │ gruzomoy.sumtel.com.ua │        0 │         2 │  35 │
└─────────────────────┴────────────┴─────────────────────┴──────────┴───────────┴────────────────────────┴──────────┴───────────┴─────┘

        Puede notar que especifiqué modificadores después del nombre de la tabla. Hago esto para asegurarme de que los datos estén completamente combinados y solo obtengo una fila por sesión.final

        A menudo se usa en el motor ClickHouse, ya que permite usar en lugar de ( generalmente más detalles en los documentos ). Con este enfoque, puede tener algunas filas por sesión en caso de actualizaciones, que luego el sistema fusiona en segundo plano. Usando modificadores, forzamos el proceso.CollapsingMergeTreeinsertsupdatesfinal

        Podemos realizar dos consultas simples para ver la diferencia.

SELECT
    uniqExact(VisitID) AS unique_sessions,
    sum(Sign) AS number_sessions, 
    -- number of sessions after collapsing
    count() AS rows
FROM datasets.visits_v1

┌─unique_sessions─┬─number_sessions─┬────rows─┐
│         1676685 │         1676581 │ 1680609 │
└─────────────────┴─────────────────┴─────────┘

SELECT
    uniqExact(VisitID) AS unique_sessions,
    sum(Sign) AS number_sessions,
    count() AS rows
FROM datasets.visits_v1
FINAL

┌─unique_sessions─┬─number_sessions─┬────rows─┐
│         1676685 │         1676721 │ 1676721 │
└─────────────────┴─────────────────┴─────────┘

        El uso tiene sus propios inconvenientes en el rendimiento. Puedes encontrar más información al respecto en la documentación .final

3. ¿Cómo garantizar la calidad de los datos?

        Verificar que no haya eventos faltantes o duplicados es muy sencillo. Puede encontrar muchas formas de detectar anomalías en los datos de series temporales, desde métodos ingenuos (por ejemplo, número de eventos dentro de +20 % o -20 % en comparación con la semana anterior) hasta ML con bibliotecas como  Prophet  o  PyCaret  .

        La consistencia de los datos es una tarea complicada. Como mencioné antes, los servicios de análisis web rastrean mucha información sobre el comportamiento de sus clientes en su sitio web. Documentan cientos de parámetros y debemos asegurarnos de que todos estos valores parezcan válidos.

        Los parámetros pueden ser numéricos (duración o número de páginas vistas) o categóricos (navegador o sistema operativo). Para los valores, podemos usar criterios estadísticos para garantizar que la distribución permanezca constante, por ejemplo, la prueba de Kolmogorov-Smirnov .

        Entonces, después de ver las mejores prácticas, mi única pregunta es cómo monitorear el acuerdo de las variables categóricas, es hora de discutirlo.

4. Variables categóricas

        Tomemos un navegador como ejemplo. Tenemos valores únicos para 62 navegadores en nuestros datos.

SELECT uniqExact(UserAgent) AS unique_browsers
FROM datasets.visits_v1

┌─unique_browsers─┐
│              62 │
└─────────────────┘

SELECT
    UserAgent,
    count() AS sessions,
    round((100. * sessions) / (
        SELECT count()
        FROM datasets.visits_v1
        FINAL
    ), 2) AS sessions_share
FROM datasets.visits_v1
FINAL
GROUP BY 1
HAVING sessions_share >= 1
ORDER BY sessions_share DESC

┌─UserAgent─┬─sessions─┬─sessions_share─┐
│         7 │   493225 │          29.42 │
│         2 │   236929 │          14.13 │
│         3 │   235439 │          14.04 │
│         4 │   196628 │          11.73 │
│       120 │   154012 │           9.19 │
│        50 │    86381 │           5.15 │
│        79 │    63082 │           3.76 │
│       121 │    50245 │              3 │
│         1 │    48688 │            2.9 │
│        42 │    21040 │           1.25 │
│         5 │    20399 │           1.22 │
│        71 │    19893 │           1.19 │
└───────────┴──────────┴────────────────┘

        Podríamos monitorear el recurso compartido de cada navegador individualmente como una variable numérica, pero en este caso estaremos monitoreando al menos 12 series de tiempo para un campo. Como todos los que han hecho una alerta al menos una vez saben, cuantas menos variables, mejor . Al rastrear muchos parámetros, hay muchas notificaciones de falsos positivos con las que lidiar.UserAgent

        Entonces comencé a pensar en una métrica que mostraría la diferencia entre las distribuciones. La idea es comparar la cuota de navegador de now() y before(). Podemos elegir el periodo anterior según la granularidad: T2T1

  •         Para datos minuciosos, puede mirar un poco,
  •         Para datos diarios, vale la pena mirar el día anterior a la semana para tener en cuenta la estacionalidad semanal,
  •         Para datos mensuales, puede ver los datos de hace un año.

        Veamos un ejemplo a continuación.

        Mi primer pensamiento fue mirar una métrica heurística similar a la norma L1 utilizada en el aprendizaje automático ( más detalles ).

        Para el ejemplo anterior, esta fórmula nos dará el siguiente resultado: 10 %. En realidad, esta métrica tiene sentido: muestra la proporción más pequeña de eventos de distribución que han cambiado los navegadores.

        Posteriormente, discutí este tema con mi jefe, quien tiene mucha experiencia en ciencia de datos. Sugirió que mirara la divergencia Kullback-Leibler o Jensen-Shannon, ya que esta es una forma más eficiente de calcular la distancia entre las distribuciones de probabilidad.

        Si no recuerdas estos indicadores o nunca has oído hablar de ellos, no te preocupes, estoy en tu lugar. Así que busqué en Google las fórmulas ( este artículo explica los conceptos a fondo) y los valores calculados para nuestro ejemplo.

import numpy as np
  
prev = np.array([0.7, 0.2, 0.1])
curr = np.array([0.6, 0.27, 0.13])

def get_kl_divergence(prev, curr):
    kl = prev * np.log(prev / curr)
    return np.sum(kl)

def get_js_divergence(prev, curr): 
    mean = (prev + curr)/2
    return 0.5*(get_kl_divergence(prev, mean) + get_kl_divergence(curr, mean))

kl = get_kl_divergence(prev, curr)
js = get_js_divergence(prev, curr)
print('KL divergence = %.4f, JS divergence = %.4f' % (kl, js))

# KL divergence = 0.0216, JS divergence = 0.0055

        Como puede ver, las distancias que calculamos varían ampliamente. Entonces, ahora que tenemos (al menos) tres formas de calcular la diferencia entre los recursos compartidos de navegador anteriores y actuales, la siguiente pregunta es qué forma elegir para nuestra tarea de monitoreo.

5. El ganador es...

        La mejor manera de estimar el rendimiento de diferentes métodos es ver cómo funcionan en la vida real. Para ello, podemos simular anomalías en los datos y comparar los efectos.

        Hay dos anomalías comunes en los datos:

  • Pérdida de datos: comenzamos a perder datos de uno de los navegadores, y todos los demás navegadores tuvieron una participación cada vez mayor
  • Cambiado: cuando el tráfico de un navegador comienza a marcar para otro. Por ejemplo, el 10% de los eventos de Safari que vemos hoy no están definidos.

        Podemos obtener el recurso compartido real del navegador y simular estas excepciones. Para simplificar, voy a agrupar todos los navegadores con una participación inferior al 5 % en grupos.browser = 0

WITH browsers AS
    (
        SELECT
            UserAgent,
            count() AS raw_sessions,
            (100. * count()) / (
                SELECT count()
                FROM datasets.visits_v1
                FINAL
            ) AS raw_sessions_share
        FROM datasets.visits_v1
        FINAL
        GROUP BY 1
    )
SELECT
    if(raw_sessions_share >= 5, UserAgent, 0) AS browser,
    sum(raw_sessions) AS sessions,
    round(sum(raw_sessions_share), 2) AS sessions_share
FROM browsers
GROUP BY browser
ORDER BY sessions DESC

┌─browser─┬─sessions─┬─sessions_share─┐
│       7 │   493225 │          29.42 │
│       0 │   274107 │          16.35 │
│       2 │   236929 │          14.13 │
│       3 │   235439 │          14.04 │
│       4 │   196628 │          11.73 │
│     120 │   154012 │           9.19 │
│      50 │    86381 │           5.15 │
└─────────┴──────────┴────────────────┘

        Es hora de simular ambas situaciones. Puedes   encontrar todo el código en GitHub . Para nosotros, el parámetro más importante es el efecto real: la proporción de eventos perdidos o modificados. Idealmente, nos gustaría que nuestras métricas fueran iguales a este efecto.

        Como resultado de la simulación, obtuvimos dos gráficos que muestran la correlación entre el efecto del hecho y la métrica de distancia.

        Cada punto del gráfico muestra el resultado de una simulación: el efecto real y la distancia correspondiente.

        Puede ver fácilmente que la norma L1 es la mejor métrica para nuestra tarea, ya que está más cerca de la línea. Kullback-Leibler y Jensen-Shannon divergen ampliamente y tienen diferentes niveles según el caso de uso (qué navegador está perdiendo tráfico).distance = share of affected events

        Dichas métricas no son adecuadas para el monitoreo porque no podrá especificar un umbral que le avise cuando se vea afectado más del 5% de su tráfico. Además, no podemos interpretar fácilmente estas métricas, mientras que la norma L1 muestra con precisión el grado de anomalía.

Seis, cálculo de la norma L1

        Ahora que sabemos qué métrica nos mostrará la consistencia de los datos, la última tarea pendiente es implementar el cálculo de la norma L1 en la base de datos (en nuestro caso, ClickHouse).

        Para ello podemos utilizar las conocidas funciones de ventana.

with browsers as (
    select
        UserAgent as param,
        multiIf(
            toStartOfHour(UTCStartTime) = '2014-03-18 12:00:00', 'previous',
            toStartOfHour(UTCStartTime) = '2014-03-18 13:00:00', 'current',
            'other'
        ) as event_time,
        sum(Sign) as events
    from datasets.visits_v1
    where (StartDate = '2014-03-18')
    -- filter by partition key is a good practice
        and (event_time != 'other')
    group by param, event_time)
select
    sum(abs_diff)/2 as l1_norm
from
    (select
        param,
        sumIf(share, event_time = 'current') as curr_share,
        sumIf(share, event_time = 'previous') as prev_share,
        abs(curr_share - prev_share) as abs_diff
    from
        (select
            param,
            event_time,
            events,
            sum(events) over (partition by event_time) as total_events,
            events/total_events as share
        from browsers)
    group by param)

┌─────────────l1_norm─┐
│ 0.01515028932687386 │
└─────────────────────┘

        ClickHouse tiene funciones de matriz muy poderosas y las he usado durante mucho tiempo antes de admitir funciones de ventana. Así que quiero mostrarles el poder de esta herramienta.

with browsers as (
    select
        UserAgent as param,
        multiIf(
            toStartOfHour(UTCStartTime) = '2014-03-18 12:00:00', 'previous',
            toStartOfHour(UTCStartTime) = '2014-03-18 13:00:00', 'current',
            'other'
        ) as event_time,
        sum(Sign) as events
    from datasets.visits_v1
    where StartDate = '2014-03-18' -- filter by partition key is a good practice
        and event_time != 'other'
    group by param, event_time
    order by event_time, param)
select l1_norm 
from
    (select
        -- aggregating all param values into arrays
        groupArrayIf(param, event_time = 'current') as curr_params,
        groupArrayIf(param, event_time = 'previous') as prev_params,
        
        -- calculating params that are present in both time periods or only in one of them
        arrayIntersect(curr_params, prev_params) as both_params,
        arrayFilter(x -> not has(prev_params, x), curr_params) as only_curr_params,
        arrayFilter(x -> not has(curr_params, x), prev_params) as only_prev_params,
        
        -- aggregating all events into arrays
        groupArrayIf(events, event_time = 'current') as curr_events,
        groupArrayIf(events, event_time = 'previous') as prev_events,
        
        -- calculating events shares
        arrayMap(x -> x / arraySum(curr_events), curr_events) as curr_events_shares,
        arrayMap(x -> x / arraySum(prev_events), prev_events) as prev_events_shares,
        
        -- filtering shares for browsers that are present in both periods
        arrayFilter(x, y -> has(both_params, y), curr_events_shares, curr_params) as both_curr_events_shares,
        arrayFilter(x, y -> has(both_params, y), prev_events_shares, prev_params) as both_prev_events_shares,
        
        -- filtering shares for browsers that are present only in one of periods
        arrayFilter(x, y -> has(only_curr_params, y), curr_events_shares, curr_params) as only_curr_events_shares,
        arrayFilter(x, y -> has(only_prev_params, y), prev_events_shares, prev_params) as only_prev_events_shares,
        
        -- calculating the abs differences and l1 norm
        arraySum(arrayMap(x, y -> abs(x - y), both_curr_events_shares, both_prev_events_shares)) as both_abs_diff,
        1/2*(both_abs_diff + arraySum(only_curr_events_shares) + arraySum(only_prev_events_shares)) as l1_norm
    from browsers)

┌─────────────l1_norm─┐
│ 0.01515028932687386 │
└─────────────────────┘

        Este enfoque puede ser útil para personas con una mente pitónica. Con persistencia y creatividad, se puede escribir cualquier lógica utilizando funciones de matriz.

7. Alertas y Monitoreo

        Tenemos dos consultas que nos muestran fluctuaciones en la proporción de navegadores en nuestros datos. Los datos de interés se pueden monitorear usando este método.

        Lo único que queda es alinearse con el equipo en el umbral de alerta. Por lo general, miro los datos históricos y las anomalías anteriores para obtener algunos niveles iniciales y luego sigo ajustándolos con nueva información: alertas de falsos positivos o anomalías perdidas.

        Además, mientras implementaba el monitoreo, me encontré con algunos matices que me gustaría cubrir brevemente:

  • Por ejemplo, hay parámetros en los datos que no tienen sentido en el monitoreo o, por lo tanto, elija sabiamente qué parámetros incluir.UserIDStartDate
  • Es posible que tenga parámetros con alta cardinalidad. Por ejemplo, en análisis web, los datos tienen más de 600 000 valores únicos. Calcular métricas para ello puede consumir recursos. Por lo tanto, sugeriría almacenar estos valores (por ejemplo, tomar dominio o TLD), o simplemente monitorear los valores de nivel superior y agrupar los otros valores en un grupo separado "Otro".StartURL
  • Puede usar el mismo marco para valores usando cubos.
  • En algunos casos, se espera que los datos cambien significativamente. Por ejemplo, si está monitoreando el campo de versión de la aplicación, recibirá una alerta después de que se publique cada versión. Dichos eventos ayudan a garantizar que su monitoreo aún esté allí :)

 

Supongo que te gusta

Origin blog.csdn.net/gongdiwudu/article/details/132338528
Recomendado
Clasificación