Explique os candidatos mais próximos

PythonBeginner
Pratique Agora

Introdução

A biblioteca já identifica jogos utilizáveis esta noite. Agora precisa de alternativas similares transparentes, com explicações suficientes para uma pessoa escolher. Você calculará distâncias com duas características públicas, mostrará diferenças com sinal nas unidades originais e manterá todos os empates na fronteira.

Este laboratório pressupõe adequação com Python e MinMax do anterior, mas começa independentemente: entradas e auxiliar completo estão prontos, sem arquivos ou processos anteriores. Você implementará o ranking e JSON reais, que o leitor não completa. Os 50 minutos são uma estimativa do autor. Similaridade de conteúdo não prevê gosto pessoal nem demonstra desempenho em catálogos grandes.

Meça cada alternativa adequada

Nesta etapa, você construirá uma tabela completa de distâncias e mostrará o que cada candidato muda em relação à referência.

Abra a aba Terminal e entre no espaço independente. Abrir ou reconectar esta entrada não reinstala o espaço de trabalho nem apaga o progresso. cd define o diretório deste terminal; repita em um terminal novo.

cd /home/labex/project/board-games

O catálogo usa chaves únicas game_id, títulos só de apresentação, duração minutes, avaliação complexity de 1 a 5 e limites inclusivos min_players/max_players. O CSV separado tem um true/false por ID em outra ordem. night.json indica o exemplo, players e k desejado. A consulta continua válida se a própria referência estiver indisponível ou não comportar o grupo; só os candidatos precisam atender à noite. Mesmo título ou características com ID diferente continuam legais, incluindo distância zero. Só o reference_id exato é excluído.

cat mostra texto sem alteração. Leia a solicitação e o auxiliar que implementa o laboratório anterior.

cat night.json
cat suitability.py

Execute essa interface existente antes de acrescentar um cálculo de distância. --mode suitable chama choose_games; o --output obrigatório nomeia o JSON a gravar. Sem outras opções de entrada, o executor lê catalog.csv, availability.csv e night.json do diretório atual. O leitor imprime somente o arquivo que você salvou.

python3 planner.py --mode suitable --output suitable.json
python3 show_results.py suitable.json

Você deve ver os seis IDs elegíveis alfabéticos left, near, raw, right, same e zero, com a escala abaixo. Este é um resultado funcional de adequação, não uma recomendação concluída: o módulo de ranking ainda tem regiões de implementação vazias.

choose_games associa disponibilidade por ID e retorna todos os IDs elegíveis com uma escala calculada em todo o catálogo, antes de filtrar. A ordem é minutes, depois complexity. scale.min é [10, 1], scale.range é [100, 4]. Reutilize os mesmos parâmetros na consulta e em todos os candidatos: emprestar um jogo não deve mover a régua. Não há rótulo-alvo nem divisão supervisionada treino/teste.

planner.py trata opções e formatos com coursekit.py, mas não escolhe nem ordena. Você pode alterar suitability.py, ranking.py e resultados; preserve os CSV/JSON, planner.py, coursekit.py, show_results.py e os dois starter-*.py.

Para um valor x, MinMax calcula (x - minimum) / range. Uma característica constante conserva amplitude zero, mas usa divisor um; como a referência pertence ao catálogo, todas essas diferenças são zero. Para consulta escalada q e candidato c, a distância euclidiana é sqrt((c_minutes-q_minutes)^2 + (c_complexity-q_complexity)^2). Os pesos são iguais depois da escala. Distância menor significa similaridade sob essa regra, não maior diversão.

A explicação original é separada: minutes_delta, complexity_delta são candidato menos referência, antes de escalar. Uma diferença negativa de minutos significa um jogo mais curto. As colunas devem vir do candidato real desse ID, não de outra linha nem ser inventadas a partir da distância.

Inspecione a estrutura de ranking antes de editá-la. from suitability import choose_games importa o auxiliar local. As linhas fornecidas prepared, by_id e reference conectam essa saída e a consulta aos registros do catálogo indexados por ID. A lista rows vazia, o laço de candidatos, a ordenação e o retorno final fornecem a estrutura ao redor. Três comentários marcam as regiões que você completará; não calculam as distâncias nem a seleção ausentes.

cat ranking.py

Faça um backup do código-fonte. cp copia o arquivo atual; este backup é a base incompleta, não uma resposta concluída.

cp ranking.py ranking.py.bak

np.where escolhe um para amplitude zero e a amplitude real nos demais casos. np.linalg.norm dá o comprimento euclidiano do vetor de duas diferenças. float converte NumPy em números compatíveis com JSON. A chave de ordenação é um par: distância não arredondada e, quando exatamente igual, game_id. A curta lambda define essa chave por linha. Não arredonde antes de ordenar. Últimas casas de fórmulas equivalentes podem alterar a ordem de valores quase iguais; decimais exibidos não definem a ordem armazenada.

Abra o módulo no nano. Como no primeiro laboratório, LC_ALL=C.UTF-8 aplica-se apenas a esta chamada do editor. Mantenha quatro espaços por nível de recuo; Python usa indentação para agrupar instruções.

LC_ALL=C.UTF-8 nano ranking.py

Pressione Ctrl+W, busque ## Metric parameters e pressione Enter. Desça até o pass seguinte, pressione Home e Ctrl+K uma vez para remover essa linha provisória. Insira estas cinco linhas com seus quatro espaços iniciais. Preserve rows = [] e a função ao redor.

    minimum = np.array(prepared['scale']['min'])
    ranges = np.array(prepared['scale']['range'])
    divisor = np.where(ranges == 0, 1, ranges)
    query = np.array([reference['minutes'], reference['complexity']])
    query_scaled = (query - minimum) / divisor

Depois pressione Ctrl+W, busque ## Candidate record e pressione Enter. Desça até o pass dessa região, pressione Home e Ctrl+K uma vez. Insira o bloco seguinte. Ele está dentro do laço existente, portanto suas instruções começam com oito espaços; os campos dentro de rows.append usam doze. Mantenha inalterados a linha for, game = by_id[game_id] e o rows.sort posterior.

        values = np.array([game['minutes'], game['complexity']])
        candidate_scaled = (values - minimum) / divisor
        distance = float(np.linalg.norm(candidate_scaled - query_scaled))
        difference = values - query
        rows.append({
            'game_id': game_id, 'title': game['title'], 'distance': distance,
            'minutes_delta': float(difference[0]),
            'complexity_delta': float(difference[1]),
        })

Deixe a região ## Cutoff selection como cutoff = None e selected = [] por enquanto. Isso torna esta tabela intermediária legível deixando honestamente a seleção incompleta. Pressione Ctrl+O, Enter e Ctrl+X para salvar e sair. Se perceber uma edição indesejada antes de salvar, Ctrl+X e N descartam esta sessão do editor; reabra e corrija apenas a região pretendida.

Execute planner.py no modo recommend; --output dá o nome real da saída. Sem outras opções, lê catalog.csv, availability.csv, night.json do diretório atual. --catalog, --availability, --request aceitam caminhos explícitos.

python3 planner.py --mode recommend --output recommendations.json

Mostre o JSON salvo com o leitor. Ele só abre esse arquivo, sem recalcular pelo catálogo ou completar linhas.

python3 show_results.py recommendations.json

Devem aparecer seis linhas. zero tem distância zero, near e same ficam perto de 0.1, left/right em 0.3. raw está perto de 0.5001, apesar de um minuto adicional, pois a complexidade aumenta dois pontos. Minutes +/- e Complexity +/- mostram diferenças originais com sinal. Esta versão deixa propositalmente Cutoff: None e seleção vazia; a próxima etapa termina a regra.

Se aparecer SyntaxError ou IndentationError, leia o arquivo e o número da linha, reabra ranking.py e corrija essa linha ou região. Não sobrescreva o módulo inteiro para reparar uma linha. Um erro de arquivo ausente geralmente significa que o terminal está no diretório errado: pwd o mostra e o cd inicial restaura o local correto, como praticado no primeiro laboratório. cp ranking.py.bak ranking.py restaura a base incompleta somente se você quiser deliberadamente abandonar o trabalho desta etapa. Preserve os arquivos de entrada em vez de editá-los para fazer um resultado passar.

Preserve o conjunto completo no limite

Nesta etapa, você terminará a seleção e usará o mesmo programa em noites com poucos candidatos e nenhum.

Continue na aba Terminal usando este diretório.

cd /home/labex/project/board-games

k é um mínimo desejado quando há candidatos suficientes, não uma promessa de exatamente k linhas. Calcule e ordene todas as distâncias elegíveis primeiro. Com N candidatos, o limite é a distância na posição min(k, N). Índices Python começam em zero, portanto o índice é min(k, N) - 1. Preserve todos com distância não arredondada até cutoff + 1e-9. A margem absoluta 1e-9 define empate do limite, não agrupamento de ordenação nem autorização para arredondar.

Com menos de k, devolva os existentes. Sem candidatos, candidates e selected_ids são listas vazias e o limite JSON é null (None em Python). Não use um jogo indisponível ou inadequado como substituto. Pegar primeiro os k mais próximos do catálogo inteiro e filtrar depois pode perder alternativas legais posteriores; a adequação prévia evita isso.

A solicitação normal tem k=2. zero é o mais próximo; near e same compartilham a fronteira segundo a regra. Selecione os três. A ordem segue as distâncias reais não arredondadas, usando game_id nos empates exatos. As seis linhas permanecem para mostrar todos os jogos considerados.

Faça um backup da tabela funcional de distâncias antes de acrescentar a seleção. Esta cópia preserva os parâmetros da métrica e os registros de candidatos que você acabou de testar.

cp ranking.py ranking-distances.py.bak

Abra o mesmo módulo, sem substituir seu trabalho anterior.

LC_ALL=C.UTF-8 nano ranking.py

Pressione Ctrl+W, busque ## Cutoff selection e pressione Enter. Desça até cutoff = None, pressione Home e Ctrl+K duas vezes para remover somente essa linha e selected = []. Insira este bloco com quatro espaços para if e else e oito dentro de cada ramo. O retorno final permanece após o bloco com seu recuo existente.

    if rows:
        cutoff = rows[min(request['k'], len(rows)) - 1]['distance']
        selected = [row['game_id'] for row in rows
                    if row['distance'] <= cutoff + 1e-9]
    else:
        cutoff = None
        selected = []

A proteção if rows impede indexar uma lista vazia. Uma compreensão de lista preserva os IDs correspondentes na ordem já classificada. Preserve os parâmetros da métrica, o laço de candidatos e a ordenação da etapa 1. Salve com Ctrl+O, Enter e saia com Ctrl+X. Se precisar desfazer esta etapa, cp ranking-distances.py.bak ranking.py restaura a versão testada da tabela de distâncias, ainda com a seleção incompleta; refaça a edição de limite em vez de repetir o arquivo inteiro.

O JSON requer scale.min, scale.range na ordem das características; por candidato, game_id, title, distance numérica, minutes_delta, complexity_delta com sinal; além de cutoff e selected_ids. Guarde números finitos na precisão calculada, não strings arredondadas. Explicações extras inofensivas são aceitas. A avaliação por ID usa tolerâncias absoluta e relativa 1e-8, distintas do empate 1e-9. Fórmulas equivalentes e escolhas normais são permitidas; gostos não são avaliados. Ambas as interfaces devem funcionar: choose_games devolve eligible_ids alfabéticos e escala completa, rank_games a recomendação. O ranking pode ser independente, sem obrigação de chamar o auxiliar internamente. Não é exigido outro arquivo de adequação no espaço de trabalho.

Gere novamente e leia o resultado normal.

python3 planner.py --mode recommend --output recommendations.json
python3 show_results.py recommendations.json

Agora zero, near, same estão selecionados e os demais continuam visíveis. cat recommendations.json mostra o JSON com as distâncias não arredondadas.

Outras duas solicitações mudam só a noite, não catálogo nem empréstimos. Examine com cat. --request vale para uma chamada, sem mudar night.json ou variáveis do terminal. Saídas separadas preservam o resultado normal.

cat sparse-night.json
python3 planner.py --mode recommend --request sparse-night.json --output sparse.json
python3 show_results.py sparse.json

Oito pessoas só podem usar right, mesmo com k=5. Um candidato honesto é o correto. A escala completa e a distância 0.3 continuam iguais.

cat empty-night.json
python3 planner.py --mode recommend --request empty-night.json --output empty.json
python3 show_results.py empty.json

Vinte pessoas não têm jogos adequados. Espere zero linhas, Cutoff: None e mensagem explícita de ausência. No Terminal, cat empty.json mostra o limite salvo como null. O leitor mostra seu resultado vazio, sem escolher substitutos.

Os três arquivos recommendations.json, sparse.json, empty.json são obrigatórios, correspondentes às solicitações e reproduzíveis com entradas intactas. Para recuperar uma edição acidental de código, reabra o nano e corrija a região afetada, preservando o restante do trabalho concluído; depois execute novamente os três comandos. cp starter-ranking.py ranking.py restaura apenas o starter incompleto para praticar novamente. Reabrir terminal não reinicia dados nem executa setup.

Resumo

Você mediu todos os candidatos elegíveis com a mesma escala do catálogo, explicou distâncias com diferenças reais com sinal nas unidades originais e selecionou todos os empates sem arredondar antes. Seus três arquivos cobrem noite normal, poucos candidatos e nenhum. A recomendação descreve similaridade transparente, não a previsão do que alguém gostará.