#python #list #sorting #playlist
#python #Список #сортировка #список воспроизведения
Вопрос:
Я пытаюсь изменить порядок элементов в списке в Python на основе текущего и следующего значения в этом списке. Я хочу использовать этот порядок для создания списка воспроизведения видео, который не будет содержать два последовательных видео определенного жанра. Список будет преобразован в список воспроизведения m3u.
Моя ситуация: я назвал все видео, которые у меня есть, следующим образом: «название фильма» — «год выпуска» — «жанр».
Например, я не хочу иметь список воспроизведения, который будет содержать два последовательных боевика. Единственное исключение из этого должно быть, когда я, например, использую каталог action movies, который содержит только action movies. Затем список воспроизведения может быть создан в случайном порядке.
В настоящее время у меня есть следующий код:
import os
import glob
from threading import Timer
cwd = os.getcwd()
# create list
videofiles = []
for file in glob.glob('**/*.mp4', recursive=True):
videofiles.append(file)
# split file on last index to compare genre
def sortSplit(file):
return file.split('- ',2)[2]
randomvideo = []
for file in videofiles:
randomvideo.append(sortSplit(file))
randomvideo.sort()
Когда я использую функцию sortSplit, я получаю индекс / строку, которую хочу сравнить. Однако у меня есть проблемы со следующими вещами:
- Будет ли разделение «запоминать» исходную строку, чтобы при создании списка он содержал полное имя файла?
- Я не могу найти способ сравнить текущие и следующие элементы в списке
Для этой последней части пример списка может быть:
[‘movie-year-ACTION’, ‘movie-year-ACTION’, ‘movie-year-SCIFI’, ‘movie-year-ДОКУМЕНТАЛЬНЫЙ фильм’]
Там, где порядок списка должен учитывать ДЕЙСТВИЕ в первом элементе, сравните его со следующим элементом и убедитесь, что они совпадают, и переключите следующий элемент на жанр, который не является ДЕЙСТВИЕМ, но может быть чем угодно, кроме этого. Поскольку я ожидаю, что количество жанров будет расти, я ищу способ, чтобы эти жанры не были зафиксированы, например, в другом списке. Опять же, единственным исключением является то, что, когда все элементы в списке относятся к жанру ACTION, просто создайте список в случайном порядке.
Конечно, я открыт для совершенно разных подходов, если они служат этой цели.
Комментарии:
1. Если я вас правильно понял, вы хотите отсортировать список файлов по годам и жанрам (сначала должен быть ACTION, затем SCI-FI и т. Д.)?
2. Порядок жанров не важен, я бы предпочел каждый раз создавать новый список с единственным правилом, согласно которому два последовательных жанра не могут быть упорядочены таким образом, за исключением того, что, когда все файлы одного и того же жанра, просто создайте случайный порядок этих файлов
3. Существуют ли какие-либо крайние случаи, например, что, если в вашем списке было 3 боевика и 1 фильм ужасов. В этом случае вы получите 2 одинаковых рядом друг с другом.
4. Я думал об этих сценариях, но пока не смог найти решение. Поскольку я ожидаю, что общий объем будет меняться со временем, теоретически может произойти подобный сценарий. Если это так, конечно, я бы согласился, что это происходит.
Ответ №1:
Алгоритм
forbidden_genre = None
While there are movies in the database:
Pick a movie from the non-forbidden genre which has largest remaining number of movies
Remove that movie from database, add it to playlist
forbidden_genre = genre of that movie
Обратите внимание, что мы всегда выбираем переход от одного из жанров с наибольшим оставшимся количеством фильмов, чтобы не загонять себя в угол в ситуации, когда в одном из жанров все еще много фильмов, а фильмов других жанров недостаточно для чередования.
Код Python
Чтобы иметь возможность выбирать фильмы по жанрам, мы начнем с группировки фильмов по жанрам, используя itertools.groupby
. В следующем коде мы можем получить доступ к названию жанра как sortedbysize_groups[genre_index][0]
и к списку оставшихся фильмов этого жанра как sortedbysize_groups[genre_index][1]
import operator # itemgetter(2)
import itertools # groupby
def make_playlist(videofiles):
groups_tmp = itertools.groupby(sorted(videofiles, key=operator.itemgetter(2)), operator.itemgetter(2))
sortedbysize_groups = sorted([(k, list(g)) for k,g in groups_tmp], key=lambda p: len(p[1]))
playlist = []
forbidden_genre = None
while len(sortedbysize_groups) > 1:
genre_index = get_next_nonforbidden_index(sortedbysize_groups, forbidden_genre)
next_film = sortedbysize_groups[genre_index][1].pop()
forbidden_genre = sortedbysize_groups[genre_index][0]
playlist.append(next_film)
if len(sortedbysize_groups[genre_index][1]) == 0:
sortedbysize_groups.pop(genre_index)
else:
move_back_if_necessary_to_keep_sorted(sortedbysize_groups, genre_index % len(sortedbysize_groups))
playlist.extend(sortedbysize_groups[0][1])
return playlist
def get_next_nonforbidden_index(sortedbysize_groups, forbidden_genre):
return (-1) if (sortedbysize_groups[-1][0] != forbidden_genre) else (-2)
def move_back_if_necessary_to_keep_sorted(sortedbysize_groups, i):
while i > 0 and len(sortedbysize_groups[i-1][1]) > len(sortedbysize_groups[-1][1]):
i -= 1
if i < len(sortedbysize_groups) - 1:
sortedbysize_groups[i], sortedbysize_groups[-1] = sortedbysize_groups[-1], sortedbysize_groups[i]
videofiles = [('Star Gate', 1994, 'scifi'), ('Good Will Hunting', 1997, 'drama'), ('A Beautiful Mind', 2001, 'drama'), ('Tenet', 2020, 'scifi'), ('Blade Runner', 1982, 'scifi'), ('The Tree of Life', 2011, 'experimental'), ('Pi', 1998, 'experimental')]
print(make_playlist(videofiles))
# [('Blade Runner', 1982, 'scifi'), ('Pi', 1998, 'experimental'), ('A Beautiful Mind', 2001, 'drama'), ('Tenet', 2020, 'scifi'), ('Good Will Hunting', 1997, 'drama'), ('Star Gate', 1994, 'scifi'), ('The Tree of Life', 2011, 'experimental')]
В случае, когда идеального решения не существует, потому что в одном жанре слишком много фильмов, алгоритм сделает все возможное, и список воспроизведения будет заканчиваться двумя или более фильмами этого жанра. Обратите внимание, что это происходит только в том случае, если строго более половины фильмов принадлежат к одному жанру.
Комментарии:
1. Прежде всего, большое вам спасибо за приложенные усилия. Я читаю это и пытаюсь извлечь из этого уроки. Однако у меня есть вопрос: когда я запускаю код, я получаю сообщение об ошибке в строке » move_back_if_necessary_to_keep_sorted(sortedbysize_groups, i % len(sortedbysize_groups))». В нем говорится, что «i» не определено. Чего мне здесь не хватает?
2. @borisvdh Похоже, версия кода в моем сообщении не такая, как на моем компьютере — теперь это должно быть исправлено. Переменная
i
была переименованаgenre_index
для большей удобочитаемости.3. Я рекомендую печатать переменную
sortedbysize_groups
, чтобы понять, что происходит, особенно если вы не знакомы сitertools.groupby
.4. Большое вам спасибо за ваши ответы и код. Я попробовал это, и это работает, так что теперь я могу внимательно изучить его и извлечь из него уроки. Я проголосовал за ответ, но новые пользователи не влияют на общедоступные оценки