From 5411e26afc11f21d03a9cefa0f5ad89222c6e81c Mon Sep 17 00:00:00 2001 From: Fedor Lyanguzov Date: Thu, 14 Oct 2021 23:08:50 +0300 Subject: [PATCH] Solve task --- README.md | 14 +++++++++---- db.sqlite3 | Bin 909312 -> 921600 bytes fuzzy_search.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 3 +++ 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 fuzzy_search.py create mode 100644 requirements.txt diff --git a/README.md b/README.md index 46a2a04..80902ae 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,16 @@ - *Выходные данные:* таблица жанров, таблица связей со столбцами (игра, жанр) - *Решение:* смотри файл scrape_genres.py +### Сопоставление базового с полным набором игр + +Необходимо реализовать быстрый неточный поиск из базового набора в полном. Предложение: построить однословные индексы по базовому и полному набору, при сопоставлении использовать расстояние Левенштейна около 3. + +Принято решение сравнивать очищенные названия триграммами. Ошибки присутствуют, для их отбора и исправления можно использовать расстояние Левенштейна. + +- *Входные данные:* таблица игр, таблица архивов +- *Выходные данные:* таблица связей со столбцами (игра, архив) +- *Решение:* смотри файл fuzzy_search.py + ## Задачи ### Выделить из базы данных кеш @@ -57,10 +67,6 @@ html-страницы складывать в отдельную базу дан Необходимо просмотреть жанры, маловстречающиеся и повторяющиеся объединить, построить примерное дерево категорий. -### Сопоставление базового с полным набором игр - -Необходимо реализовать быстрый неточный поиск из базового набора в полном. Предложение: построить однословные индексы по базовому и полному набору, при сопоставлении использовать расстояние Левенштейна около 3. - ### Построение результирующего набора игр Необходимо решить задачу минимизации критериальной функции с ограничением в 15 Гб размера на основе критериев: diff --git a/db.sqlite3 b/db.sqlite3 index 7acc4a2d8931bd20a4b7732609309c860fd5e6a0..662e62c32036189ccb90c91309c0a442ccbb9a04 100644 GIT binary patch delta 5351 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 B8CU=S delta 117 zcmZozV9{{Ee1f!K0s{lXJq82_oTy_gnZTeIoWRTfgMp1tf`Q+Q|0v&Gep$XUK8ekW z3JZ9fCHUGU_!!$I_?X%y_?X)z_*mK{_*mN|_}JPd_}JSe_&C}n_&D1o__*37__*68 N_;|KU@bSL52mrv980r83 diff --git a/fuzzy_search.py b/fuzzy_search.py new file mode 100644 index 0000000..7673aa3 --- /dev/null +++ b/fuzzy_search.py @@ -0,0 +1,51 @@ + +import sqlite3 +from ngram import NGram + +tr_table = str.maketrans('','',' !$&\'()+,-.=@[]') + +def key(s): + """ + !$&'()+,-.0123456789=@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]abcdefghijklmnopqrstuvwxyz + """ + return s.removesuffix('.zip').lower().translate(tr_table) + +def test_key(): + """ + 1 vs 100 (Europe) [b].zip + 1 vs 100 (europe) [b] + """ + s = '1 vs 100 (Europe) [b].zip' + assert key(s) == '1vs100europeb', key('1 vs 100 (Europe) [b].zip') + +def select(name, xs): + while True: + print(f'?: {name}') + for i, ((_, name), _) in enumerate(xs,1): + print(f'{i}. {name}') + inp = input('1-9,n: ') + if inp=='': + return xs[0] + if inp=='n': + raise ValueError + if inp in set('123456789'): + return xs[int(inp)-1] + +if __name__=='__main__': + test_key() + with sqlite3.connect("db.sqlite3") as db: + G = NGram(key=lambda x: key(x[1])) + for game in db.execute("SELECT ROWID, zip_name FROM archive;"): + G.add(game) + + db.execute("DROP TABLE IF EXISTS game_to_archive;") + db.execute("CREATE TABLE game_to_archive (id_game INTEGER, id_archive INTEGER);") + for id_game, name in db.execute("SELECT ROWID, name FROM top_games;"): + try: + ((id_a1, n1), p1), ((id_a2, n2), p2) = G.search(name)[:2] + if p1/p2<1.05: + (id_a1, n1), p1 = select(name, G.search(name)[:9]) + db.execute("INSERT INTO game_to_archive VALUES (?, ?);", (id_game, id_a1)) + except: + db.execute("INSERT INTO game_to_archive VALUES (?, NULL);", (id_game,)) + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..32519a9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +ngram +requests +lxml