Construa uma tabela de comparação completa

Big DataBeginner
Pratique Agora

Introdução

A loja agora distingue um par sessão/produto visualizado de eventos repetidos. A próxima pergunta é quais produtos têm grande proporção de sessões visualizadas sem adição válida e quantas observações sustentam cada comparação.

Você produzirá uma tabela completa com denominador de sessões visualizadas, quantidade concluída, quantidade incompleta e proporção incompleta. Mantenha produtos sem adições válidas e todos os empates. O resultado é uma pista de observação para investigação, não prova de que um produto ou página causou a interrupção da visita.

Este lab independente fornece o módulo de jornadas concluído no lab guiado anterior e novos arquivos brutos. Você deve conhecer as regras de primeira visualização, sessão/produto e tempo daquele lab, além de agrupamento Spark e Parquet. Use Terminal. O lote é finito e encerrado; identificadores e horários UTC em segundos são válidos, e IDs repetidos são cópias exatas. A estimativa do autor é de 35–45 minutos. Não há streaming, estimativa de pessoas únicas ou inferência causal.

Construa a comparação completa de produtos

Agregue uma linha por jornada visualizada em uma linha por produto. Comece em Terminal. Entre os programas fornecidos, edite apenas analysis.py. Preserve arquivos brutos e run_analysis.py, read_result.py, support.py sem alterações; resultados derivados pertencem a output/:

cd /home/labex/project
cat data/batch.csv
cat analysis.py

Este módulo contém read_events, build_views e build_journeys, preparados independentemente a partir do lab anterior. Um módulo contém funções reutilizáveis. O driver fornece a SparkSession atual e o caminho de entrada, depois passa o DataFrame de jornadas retornado à nova função build_report(journeys). Nada de uma VM ou sessão anterior é necessário.

Relembre o contrato: cópias completas idênticas são removidas; cada (session_id, product_id) visualizado usa a primeira visualização; uma adição só é válida para ambas as mesmas chaves no instante dessa visualização ou depois. Várias adições válidas ainda produzem uma jornada concluída. O driver usa local[2], UTC e o mesmo Python para driver e workers. Grava as saídas após as funções retornarem.

Para um produto com três pares visualizados e dois concluídos, calcule manualmente três sessões visualizadas, duas com carrinho, uma sem carrinho e proporção sem carrinho de 1/3. Uma proporção sem esse denominador poderia fazer uma única visita parecer tão convincente quanto uma grande amostra.

count("*") agora conta linhas de jornadas, não eventos brutos. Converter o Boolean completed para long mapeia true para 1 e false para 0. O sum do Spark soma esses valores. Subtrair a quantidade concluída dá a incompleta; dividir pelo número positivo de sessões visualizadas dá uma proporção de zero a um. Cada grupo aqui tem ao menos uma visualização. Um produto apenas com adições não tem grupo nem denominador; não inventamos proporção com denominador zero.

Escreva apenas a agregação por produto

Preserve read_events, build_views e build_journeys fornecidos: são o trabalho concluído do laboratório anterior. Não os digite novamente. F é seu alias existente para funções Spark. Abra o mesmo módulo com nano:

nano analysis.py

Use as setas para chegar ao final de build_journeys e deixe uma linha em branco. Como praticado, Ctrl+W busca um nome de função; cada nova declaração começa na margem esquerda e seu corpo usa quatro espaços. Você escreverá build_report completo, não preencherá um modelo de resposta preparado.

Primeiro agrupe o DataFrame de jornadas por produto. Obtenha duas contagens na mesma agregação: todas as linhas para o denominador de visualizações e depois a soma da conclusão booleana convertida em inteiro. Preserve os nomes dos campos usados pelo driver:

def build_report(journeys):
    counts = journeys.groupBy("product_id").agg(
        F.count("*").alias("view_sessions"),
        F.sum(F.col("completed").cast("long")).alias("cart_sessions"),
    )

Continue dentro da função. withColumn deriva uma coluna nomeada das contagens agrupadas. Subtraia as concluídas das visualizadas antes de dividir pelo mesmo denominador positivo de visualizações. Não filtre primeiro por conclusão: isso apagaria produtos sem adições válidas. Não selecione apenas um máximo: todos os produtos empatados pertencem ao relatório.

    return counts.withColumn(
        "no_cart_sessions", F.col("view_sessions") - F.col("cart_sessions")
    ).withColumn(
        "no_cart_ratio", F.col("no_cart_sessions") / F.col("view_sessions")
    )

Salve com Ctrl+O, confirme analysis.py com Enter e saia com Ctrl+X. Reabra para examinar a função salva e verificar se as funções anteriores permanecem:

nano analysis.py

Use Ctrl+W para encontrar build_report. Em caso de erro, use as setas e Backspace/Delete para substituir apenas a linha afetada; não acrescente uma segunda definição. Preserve a indentação de continuação e os parênteses de fechamento. Salve as alterações, saia e leia o arquivo:

cat analysis.py

Se a próxima execução informar SyntaxError ou IndentationError, localize a linha indicada no nano, confira a função ao redor, os parênteses e a indentação de quatro espaços, salve e execute novamente. Uma contagem ou proporção calculada com sucesso, mas incorreta, exige conferir o agrupamento, a conversão de tipo e o denominador. Não reinicie a VM nem reescreva as funções funcionais de G1 para reparar o relatório.

Execute a etapa de relatório com caminhos explícitos de entrada e saída:

bigdata-python run_analysis.py data/batch.csv output/main --stage report

Espere esta tabela lógica completa; números de ponto flutuante podem ser exibidos com mais dígitos:

product_id  view_sessions  cart_sessions  no_cart_sessions  no_cart_ratio
card        1              0              1                 1.0
pin         1              0              1                 1.0
tea         3              1              2                 0.6666666666666666
book        2              1              1                 0.5
mug         3              2              1                 0.3333333333333333

O driver lê o Parquet realmente salvo antes de apresentá-lo. A tabela ordena por no_cart_ratio decrescente e depois por identificador do produto apenas para facilitar a leitura dos empates. Tanto card quanto pin permanecem; empate na ordenação não permite descartar nenhum. O produto poster, apenas com adição, fica excluído por não ter par visualizado.

Os resultados são conjuntos tipados em output/main/journeys e output/main/report. O segundo tem identificador textual de produto, contagens inteiras e proporção numérica. Nomes e quantidade de partes Parquet são detalhes de implementação, não a identidade do relatório. Expressões Spark DataFrame/SQL equivalentes são aceitas; mantenha um pipeline desde a entrada fornecida e transformações dentro do Spark. Não colete jornadas brutas em Python, não mude para RDD/pandas nem faça checkpoint e recriação.

Leia os arquivos e reconstrua o snapshot

Leia o pequeno CSV e substitua o snapshot derivado sem alterar sua origem. Continue em Terminal, em /home/labex/project.

O driver preparado salva output/main/report.csv como um único CSV UTF-8 pequeno para um colega. Coleta somente a tabela final de produtos já agregada para serialização, depois que build_report retorna. Essa apresentação final não substitui as transformações Spark. Veja o arquivo real:

cat output/main/report.csv

O cabeçalho contém os mesmos cinco campos do relatório Parquet. Confira os cinco produtos, as contagens zero de sessões com carrinho para card e pin e ambas as proporções empatadas em 1.0. CSV pode ser lido como linhas e campos; contar linhas ou confiar no nome não provaria essas relações.

Use o leitor fornecido para consumir ambos os conjuntos Parquet reais e o CSV. read_result.py lê o caminho de saída informado, formata horários dentro do Spark em UTC e compara o conteúdo CSV com o relatório armazenado; não cria nem preenche resultados ausentes:

bigdata-python read_result.py output/main

Espere dez linhas de jornadas, cinco de produtos e CSV matches stored report. Se faltar um arquivo, execute a análise com entrada e destino corretos; o leitor falha intencionalmente em vez de inventar uma resposta.

sha256sum imprime uma impressão digital do conteúdo de cada arquivo bruto. O padrão *.csv seleciona os CSVs de data/. Compare essas linhas antes e depois da reconstrução:

sha256sum data/*.csv
bigdata-python run_analysis.py data/batch.csv output/main --stage report
bigdata-python read_result.py output/main
sha256sum data/*.csv

A mesma entrada dá o mesmo relatório lógico, sem produtos ou jornadas duplicados. As impressões dos arquivos brutos devem permanecer iguais. Novos nomes ou bytes Parquet podem variar entre execuções; o conteúdo lógico é o contrato de repetibilidade. O writer substitui apenas esse destino derivado conhecido, não arquivos brutos nem outro diretório de resultados.

Agora use o módulo salvo com a amostra normal e outro destino:

bigdata-python run_analysis.py data/normal.csv output/normal --stage report
bigdata-python read_result.py output/normal

Espere duas linhas de produtos, cada uma com uma sessão visualizada, uma com carrinho, zero sem carrinho e proporção 0. O resultado principal permanece intacto. As mesmas funções parametrizadas atendem aos dois lotes.

card e pin merecem atenção neste pequeno lote, mas cada um tem apenas uma sessão observada. Uma proporção alta não explica por que alguém parou, não identifica pessoas únicas nem garante a continuidade do padrão. Contagens e todos os empates tornam a observação examinável; uma afirmação causal exigiria mais evidências.

A avaliação lê os resultados reais sem reconstruí-los. Também carrega o módulo inalterado e chama suas funções em um lote descartável com identificadores e ordem diferentes, observa a linhagem real de entrada Spark e consome saídas descartáveis. Não avalia grafia exata de comando, plano escrito pelo aluno ou marcador de nome da aplicação, e nunca repara o resultado real. Não faça gravações nas funções; a serialização pertence ao driver.

Resumo

Você construiu e leu comparações completas em Parquet e CSV, manteve produtos sem adições e empates e reconstruiu um snapshot derivado preservando as entradas brutas. O denominador e as contagens explicam cada proporção. Esses padrões de sessões encerradas apoiam observação adicional, não uma afirmação de causalidade.