@denislysenko
data engineer

Какой подход для фильтрации таблиц лучше?

Всем привет. Извиняюсь за большой вопрос, но, пожалуйста, дочитайте до конца. Заранее спасибо.

Мое задание заключается в том что у меня есть два файла: movies.csv и rating.csv (первый на 10 000 строк, второй на 100 000 строк)

Файл movies.csv выглядит так:
movieId,title,genres
1,Toy Story (1995),Adventure|Animation|Children|Comedy|Fantasy
2,Jumanji (1995),Adventure|Children|Fantasy
3,Grumpier Old Men (1995),Comedy|Romance
4,Waiting to Exhale (1995),Comedy|Drama|Romance
5,Father of the Bride Part II (1995),Comedy
6,Heat (1995),Action|Crime|Thriller
7,Sabrina (1995),Comedy|Romance
8,Tom and Huck (1995),Adventure|Children
9,Sudden Death (1995),Action
...

Файл rating.csv выглядит так:
userId,movieId,rating,timestamp
1,1,4.0,964982703
1,3,4.0,964981247
1,6,4.0,964982224
1,47,5.0,964983815
1,50,5.0,964982931
1,70,3.0,964982400
1,101,5.0,964980868
2,110,4.0,964982176
2,151,5.0,964984041
2,157,5.0,964984100
3,163,5.0,964983650
3,216,5.0,964981208
3,223,3.0,964980985
...

В файле movies.csv содержится информация о разных фильмах, в rating.csv инофрмация о отзывах на эти фильмы.
Мне нужно на чистом питоне ( без pandas и SQL, можно пользоваться только уже встроеными функциями в python) написать консольную утилиту (определить top n наиболее рейтинговых фильмов (по средней оценке) для каждого указанного жанра), результатом будет csv-like датасет с заголовком genre, title, year, rating)

аргументы командной строки:
-N - число наиболее рейтинговых фильмов для каждого жанра. опциональный
-genres - фильтр по жанрам, задаваемый пользователем. может быть множественным. например, Comedy|Adventure или Comedy&Adventure. опциональный
-year_from
-year_to - фильтр на годы выпуска фильмов. опциональный
-regexp - фильтр (регулярное выражение) на название фильма. опциональный

если какие-то аргументы не указаны, то соответствющего фильтра нет.
если вообще ничего не указано, то выводятся все фильмы с сортировкой по жанру и среднему рейтингу (в порядке убывания)

вот то, что я написал, но это медленно работает
import argparse
import csv

parser = argparse.ArgumentParser()
parser.add_argument('-N',
                    type=int,
                    help='the number of the highest rated films for each genre'
                    )
parser.add_argument('-genres',
                    type=str,
                    help='filter by genre'
                    )
parser.add_argument('-year_from',
                    type=int,
                    help='filter by year (FROM YEAR)',
                    default=1800
                    )
parser.add_argument('-year_to',
                    type=int,
                    help='filter by year (TO YEAR)',
                    default=2025
                    )
parser.add_argument('-regexp',
                    type=str,
                    help='filter on the movie name'
                    )
args = parser.parse_args()

data_m = []
with open('files/movies.csv', encoding='utf-8') as file:
    reader = csv.reader(file, delimiter=',')
    for row in reader:
        data_m.append(row)
data_movies = data_m[1:]

data_r = []
with open('files/ratings.csv', encoding='utf-8') as file:
    reader = csv.reader(file, delimiter=',')
    for row in reader:
        data_r.append(row)
data_rating = data_r[1:]





result = []  # сюда будет добавляться средний рейтинг по фильмам
for row in data_movies:
    film_ID = row[0]
    sum_rating = 0.0
    count = 0.0
    for line in data_rating:
        if film_ID == line[1]:
            sum_rating += float(line[2])
            count += 1
    if count != 0.0:
        result.append(sum_rating / count)
    else:
        result.append(0)

# добавление в таблицу среднего рейтинга и года
for i in range(len(data_movies)):
    data_movies[i].append(result[i])
    data_movies[i].append(data_movies[i][1][-5:-1])

# сейчас к data_movies добавлен средний рейтинг к каждому фильму и год выпуска фильма
# вывожу в консоль эту таблицу
for row in data_movies:
    print(row)

"""
Короче, сейчас у нас есть data_movies таблица, где добавлен средний рейтинг и год к каждому фильму
!!! НО ЭТО ОЧЕНЬ МЕДЛЕННО РАБОТАЕТ
"""


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

К этой итоговой таблице я планировал применять фильтры (из аргументов командной строки) и выводить результат в консоль так, как мне показалось довольно удобным иметь готовую таблицу со средним рейтингом и просто применять к этой таблице различные условия основываясь на аргументах командной строки.

НО, ПРОСТО СДЕЛАТЬ УДОБНУЮ ТАБЛИЦУ - ОЧЕНЬ МЕДЛЕННО РАБОТАЕТ

КАКОЙ ПОДХОД ДЛЯ РЕШЕНИЯ ЭТОЙ ЗАДАЧИ ПРИМЕНИЛИ БЫ ВЫ, КАКИМ СПОСОБОМ РЕШИЛИ БЫ ЭТУ ЗАДАЧУ БЕЗ ИСПОЛЬЗОВАНИЯ СТОРОННИХ БИБЛИОТЕК?
Заранее, спасибо
  • Вопрос задан
  • 173 просмотра
Решения вопроса 1
Vindicar
@Vindicar
RTFM!
Потому что ты для каждого фильма проходишь всю таблицу рейтингов да ещё и парсишь числа каждый раз. Ещё бы оно не тормозило.
Не проще ли для каждого фильма хранить сумму рейтингов и их количество, а потом один раз пройти таблицу рейтингов и заполнить эти хранилища?
from collections import defaultdict
total = defaultdict(float)
count = defaultdict(int)
for line in data_rating:
    total[line[1]] += float(line[2])
    count[line[1]] += 1

averages = { id: total[id]/count[id] for id in count } #средние рейтинги
#и сортируем список фильмов по убыванию рейтинга
data_movies.sort(key=lambda item: averages.get(item[1], 0), reverse=True)
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы