Отделите подходящие игры от похожих

PythonBeginner
Практиковаться сейчас

Введение

Небольшая игротека ищет замену для сегодняшнего вечера. Игра может напоминать любимую, но быть недоступной или не подходить по числу участников. Здесь вы разделите два вопроса: какие игры доступны для использования и как сравнивать их числовые свойства.

Вы создадите повторно запускаемую программу отбора и настоящий JSON с результатом. Нужны основы функций и словарей Python, массивов NumPy и чтения таблиц. Знакомство с классификацией по ближайшим соседям полезно, но предсказываемых меток здесь нет. Python и NumPy подготовлены; scikit-learn не требуется. 45 минут — оценка автора с учётом чтения и обычных исправлений.

Определите игры для сегодняшнего вечера

На этом шаге вы изучите настоящий каталог игротеки, а затем реализуете правила пригодности для сегодняшнего вечера в одном участке подготовленной функции.

Откройте вкладку Terminal. Все команды, включая просмотр сохранённых файлов, выполняются здесь. Открытие или переподключение Terminal не переустанавливает и не сбрасывает рабочую среду. cd меняет текущий каталог этого терминала; у другого терминала свой каталог.

cd /home/labex/project/board-games

Начните с настоящих входных данных. CSV содержит заголовок и строки, разделённые запятыми. cat выводит файл, не изменяя его. Это полный, неотфильтрованный каталог, а не список игр, уже признанных подходящими для вечера.

cat catalog.csv

Каждая строка представляет отдельное издание игры. Его уникальный непустой game_id — ключ для поиска и связывания этого издания. title — отображаемый текст: у нескольких изданий Harbor разные ID, и они должны оставаться отдельными. minutes — положительная оценка длительности; complexity — синтетическая оценка от 1 до 5, где большее значение означает более сложную игру. Целочисленные min_players и max_players включают обе границы. Синтетические входы полны и содержат одну строку на ID; исправление некорректных данных не входит в упражнение.

cat availability.csv

В отдельной таблице выдач есть одна строка для каждого ID каталога, но в другом порядке. Текст CSV true/false превращается в булевы значения Python True/False в подготовленном загрузчике. Связывание по ключу означает поиск одинакового game_id, а не присоединение первой строки к первой. Уникальные ключи и одна строка состояния выдачи на ID предотвращают неоднозначные или отсутствующие соответствия. Вы реализуете этот поиск.

JSON хранит именованные значения, списки и объекты. Прочитайте запрос на вечер:

cat night.json

reference_id — образец для поиска сходства, players — размер группы, а k — желаемое число близких альтернатив, используемое в следующей работе. Образец служит запросом и сам может быть недоступен или не подходить группе; условия вечера обязательны только для возвращаемых альтернатив. Исключайте лишь его точный ID, а не все игры с тем же названием или одинаковыми признаками. Обзор исходных входов ещё не применяет эти правила и не создаёт результат пригодности.

Рассмотрим три доступные игры на четверых: Harbor длится 60 минут и имеет сложность 3, Quay — 61 минуту и сложность 5, Bridge — 90 минут и сложность 3. Разности в исходных единицах — кандидат минус образец: Quay дольше на минуту и сложнее на два пункта; Bridge дольше на тридцать минут при той же сложности. Параметр Python -c выполняет вычисление в кавычках как один аргумент. Точка с запятой разделяет две инструкции Python.

python3 -c "print('Quay minutes:', 61 - 60); print('Bridge minutes:', 90 - 60)"

Ожидаются 1 и 30. Исходное евклидово расстояние возводит разности признаков в квадрат, складывает их и извлекает квадратный корень. Тридцать минут могут подавить два пункта сложности, если прямо смешать эти единицы. Далее вы определите общий масштаб; сначала решите, кто допускается к сравнению.

Можно менять suitability.py, ranking.py, свои результаты и собственные резервные копии скриптов. Сохраните все предоставленные CSV/JSON, planner.py, coursekit.py, show_results.py и обе восстановительные копии starter-*.py. planner.py выбирает открытую функцию и поручает чтение файлов, преобразование типов и запись JSON модулю coursekit.py; ни один из них не принимает бизнес-решение. В этой работе оставьте ranking.py незавершённым.

Прочитайте подготовленный модуль:

cat suitability.py

choose_games(catalog, availability, request) получает загруженные списки словарей и словарь запроса. Два помеченных участка начинаются с eligible = [] и scale = {}. Это незавершённые заглушки, а не утверждение, что подходящих игр нет. Последний возврат упаковывает ID и масштаб для запускателя. Сейчас вы замените участок пригодности, оставив заглушку масштаба для шага 2. Подготовка среды и обзор входов не создают оцениваемого результата.

Создайте резервную копию модуля. cp SOURCE DESTINATION копирует только указанный файл; .bak — суффикс резервной копии. Не перезаписывайте её после ошибки.

cp suitability.py suitability.py.bak

nano редактирует существующий файл внутри Terminal. Локальное для команды LC_ALL=C.UTF-8 выбирает UTF-8 только для этого редактора, не меняя глобальные настройки оболочки. В строке сочетаний nano символ ^ означает удерживать Ctrl. Стрелки перемещают курсор; ввод вставляет текст. Ctrl+W, поисковая фраза и Enter находят текст. Home перемещает в начало строки; Ctrl+K вырезает всю эту строку. Python использует отступы: строки ниже начинаются с четырёх пробелов внутри функции, а внутри цикла и условия отступы увеличиваются.

LC_ALL=C.UTF-8 nano suitability.py

Сначала потренируйтесь отказываться от нежелательной правки. Найдите ## Candidate identities с помощью Ctrl+W и Enter, нажмите End и добавьте temporary к этому комментарию. Нажмите Ctrl+X, затем N на вопрос о сохранении. Вы выйдете, не изменив файл. Откройте его снова:

LC_ALL=C.UTF-8 nano suitability.py

Найдите eligible = [], нажмите Home, затем один раз Ctrl+K, чтобы удалить только эту строку-заглушку. Сохраните заголовок функции, участок масштаба и последний возврат. Вставьте на это место следующие строки с отступами. Словарное включение строит соответствие ID и доступности. Цикл использует and для трёх независимых требований; цепочки сравнений <= включают обе границы игроков. append добавляет подходящий ID, а не отображаемое название.

    status = {row['game_id']: row['available'] for row in availability}
    eligible = []
    for game in catalog:
        different_id = game['game_id'] != request['reference_id']
        fits_group = game['min_players'] <= request['players'] <= game['max_players']
        if different_id and fits_group and status[game['game_id']]:
            eligible.append(game['game_id'])

Сохраните с помощью Ctrl+O, нажмите Enter, чтобы подтвердить прежнее имя файла, затем Ctrl+X для выхода. Сохранение не запускает функцию. Существующий возврат использует sorted(eligible) для алфавитного списка ID независимо от порядка строк и оставляет scale пустым объектом.

Параметр запускателя --mode suitable выбирает choose_games; обязательный --output suitable.json задаёт сохранённый результат. По умолчанию используются catalog.csv, availability.csv и night.json в этом каталоге. --catalog, --availability и --request позволяют выбрать другие пути. Эти входы остаются только для чтения.

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

Предоставленный просмотрщик открывает лишь этот сохранённый JSON. Он не создаёт отсутствующий файл и ничего не вычисляет по каталогу.

python3 show_results.py suitable.json

Ожидаются Scale: {} и ID left, near, raw, right, same, zero. И same, и zero — другие издания Harbor; у zero даже те же числовые признаки, что у образца. blocked недоступен, small не вмещает четверых, а ref — точный образец. Сравните эти ID с двумя входными таблицами. Отбор уже завершён, но масштабирование намеренно ещё не реализовано.

Следующая ошибка каталога намеренная. Перейдите на один каталог вверх:

cd /home/labex/project
python3 planner.py --mode suitable --output suitable.json

Python сообщает can't open file и No such file or directory: здесь он не находит запускатель. Существующая программа и результат не сброшены. pwd показывает каталог этого терминала; вернитесь в рабочую папку и повторите настоящую команду.

pwd
cd /home/labex/project/board-games
python3 planner.py --mode suitable --output suitable.json
python3 show_results.py suitable.json

При ошибке редактирования прочитайте указанные Python имя файла и номер строки, снова откройте модуль в nano и исправьте лишь затронутый участок. SyntaxError часто указывает на недостающие двоеточие или кавычку; IndentationError означает, что нужно проверить ведущие пробелы. Сохраните файл, выйдите и повторите запуск. Ctrl+X, N отбрасывает несохранённые правки. При необходимости cp suitability.py.bak suitability.py восстанавливает только эту незавершённую основу, поэтому затем придётся снова реализовать участок пригодности. Ни резервная копия, ни защищённый starter-suitability.py не являются готовым решением. Не запускайте подготовку среды заново и не меняйте входы для восстановления.

Сохраните единый масштаб всего каталога

На этом шаге вы реализуете участок масштаба, сохраняя завершённую логику пригодности.

Продолжайте во вкладке Terminal. Укажите каталог явно, чтобы команды работали и в новом терминале.

cd /home/labex/project/board-games

Признак — числовое свойство для сравнения. Порядок всегда minutes, затем complexity; доступность и число игроков определяют пригодность, а не расстояние. Найдите минимум и максимум по всему каталогу, включая образец, недоступные и неподходящие игры. Диапазон равен максимуму минус минимум. MinMax преобразует x в (x - minimum) / range, представляя разности как доли диапазонов своих признаков.

Полные минимумы — [10, 1], диапазоны — [100, 4]. Для Quay получаем [1/100, 2/4], для Bridge [30/100, 0/4]. Масштабированное расстояние Quay около 0.5001, Bridge — 0.3. Метрика меняет порядок, но никакие единицы не раскрывают личные вкусы. Равные веса после масштабирования — открытое решение этого упражнения.

Каталог здесь определяет метрику, а не обучает предсказание с учителем. Будущих меток нет, поэтому не нужно разделение на обучающую и тестовую выборки. Изменение только доступности не должно менять масштаб или расстояния оставшихся кандидатов. Пересчёт после фильтра двигал бы линейку при каждой выдаче.

Если признак постоянен во всём каталоге, диапазон нулевой. Сохраните настоящий 0, но используйте делитель 1 в дальнейшем расстоянии. Образец тоже входит в каталог, поэтому все разности по этому признаку нулевые и не вносят расстояния. Это предотвращает деление на ноль без выдумывания информации.

Сохраните новую резервную копию работающего этапа отбора, не трогая исходную копию основы:

cp suitability.py suitability-ids.py.bak
LC_ALL=C.UTF-8 nano suitability.py

Найдите scale = {} с помощью Ctrl+W и Enter, нажмите Home и вырежьте только эту строку-заглушку с помощью Ctrl+K. Оставьте завершённые поиск ID и цикл выше без изменений. Вставьте четыре строки с отступами ниже. np.array создаёт числовую таблицу с двумя столбцами в опубликованном порядке признаков; axis=0 находит минимумы и максимумы вдоль каждого столбца. tolist() превращает массивы в обычные списки, которые можно сохранить в JSON.

    features = np.array([[game['minutes'], game['complexity']] for game in catalog])
    minimum = features.min(axis=0)
    ranges = features.max(axis=0) - minimum
    scale = {'min': minimum.tolist(), 'range': ranges.tolist()}

Существующий возврат теперь включает вычисленный scale и те же отсортированные ID. Сохраните с помощью Ctrl+O, Enter, затем Ctrl+X. Вы добавили участок, сохранив предыдущую работу, без добавления ещё одной программы.

Сохраняются числа, не форматированные строки. Список подходящих идентификаторов содержит каждый допустимый ID один раз по алфавиту. scale.min и scale.range содержат по два числа в порядке признаков. Допускаются дополнительные безвредные пояснения. Абсолютная и относительная числовые погрешности проверки — 1e-8: они допускают обычные ошибки плавающей точки, не другую метрику.

Заново создайте настоящий результат из исходных входов, перезаписывая только собственный выход.

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

Ожидаются минимумы 10 и 1, диапазоны 100 и 4 и те же шесть подходящих ID. Просмотрщик может показывать 10.0 вместо 10; оба означают число десять. Изучите сохранённую структуру напрямую:

cat suitable.json

Если правка повредит участок пригодности, cp suitability-ids.py.bak suitability.py восстановит только этот работающий этап. Снова откройте nano и реализуйте участок масштаба. Вместо этого cp starter-suitability.py suitability.py восстанавливает незавершённую основу и теряет оба участка; используйте эту команду, лишь намеренно начиная заново. Обычные исправления должны менять соответствующие строки, после чего нужно сохранить файл, выйти и повторить запуск. Сохраните оба предоставленных starter-файла и все входы. Переподключение Terminal никогда не запускает подготовку среды и не восстанавливает резервную копию за вас.

Самопроверка: пригодность для вечера может измениться, не меняя эту линейку полного каталога. В следующей независимой работе предоставлена полная функция пригодности, чтобы вы сосредоточились на расстояниях и отборе.

Резюме

Вы отделили пригодность для вечера от сходства, связали независимые записи по стабильным ID, сохранили допустимые одноимённые и численно идентичные альтернативы и получили фиксированный масштаб MinMax по всему каталогу. suitable.json — настоящий воспроизводимый результат. Следующая работа использует эти знания для измерения и объяснения кандидатов, но запускается в собственной независимой среде.