создание вложенных списков из списка строк, содержащих заданное максимальное количество слов

#python #string #list #python-3.8

Вопрос:

У меня есть список строк. Я хочу создать вложенные списки из списка таким образом, чтобы он содержал строки из исходного списка, но так, чтобы количество слов в каждом вложенном списке было меньше 16, а предыдущая строка должна быть первым элементом вложенного списка, за исключением первого подсписка.

Чтобы привести пример, предположим, что мой список приведен ниже и содержит 5 строк, каждая из которых содержит различное количество слов.

 qq = ['blended e learning forumin planning', 'difficulties of learning as forigen language', 'difficulties of grammar', 'students difficulties in grammar', 'difficulties of english grammar']
 

Я хочу создать вложенные списки, удовлетворяющие вышеуказанному условию, максимум с 16 словами, и каждый список содержит предыдущую строку в качестве первого элемента (кроме первого вложенного списка). Там будет только два подсписка, и мой вывод будет

 q1 = ['blended e learning forumin planning', 'difficulties of learning as forigen language', 'difficulties of grammar']
q2 = ['difficulties of grammar', 'students difficulties in grammar', 'difficulties of english grammar']
 

Это то, что я пробовал. Правильно ли это и есть ли лучший способ сделать это ? У меня есть миллионы списков для выполнения этой операции.

 qq = ['blended e learning forumin planning', 'difficulties of learning as forigen language', 'difficulties of grammar', 'students difficulties in grammar', 'difficulties of english grammar']

psz = 0
pi = 0
msz = 16
subqq = list()
qq_i = list()

for i in range(len(qq)):
    csz=psz len(qq[i].split())
    if (csz>msz):
        subqq.append(qq_i.copy())
        qq_i.clear()
        qq_i.append(qq[i-1])
        qq_i.append(qq[i])
        psz = 0
    else:
        qq_i.append(qq[i])
        psz  = len(qq[i].split())

subqq.append(qq_i)
 

Комментарии:

1. Что вы пробовали до сих пор?

2. @Phydeaux Я тоже добавил свой код.

Ответ №1:

Вот что я придумал. Это алгоритм, аналогичный вашему и ответу Соруша Бахтиария, но он не должен содержать ошибок в подсчете слов, и я думаю, что его легче читать.

Это также приводит к ошибке в случае, когда мы начинаем новый подсписок с последней фразы в предыдущем и не можем добавить следующую фразу, не нарушив ограничение на количество слов. Это может произойти, если есть две последовательные фразы, в которых >8 слов — если вы можете быть уверены, что этого никогда не произойдет, вы можете опустить эту часть.

 def count_words(phrase):
    return len(phrase.split())


def sublists_with_max_words(main_list, max_words=16):
    output_sublists = []

    current_sublist = []
    current_sublist_words = 0

    for phrase in main_list:
        words_in_phrase = count_words(phrase)

        if (current_sublist_words   words_in_phrase) > max_words:
            # If we cannot add the phrase to the sublist without breaking
            # the word limit, then add the sublist to the output
            output_sublists.append(current_sublist)

            # Start a new sublist with the last phrase we added
            last_phrase = current_sublist[-1]
            current_sublist = [last_phrase]
            current_sublist_words = count_words(last_phrase)

            # If we cannot add the phrase to the new sublist either, then raise
            # an exception as we cannot continue without breaking the word limit
            if (current_sublist_words   words_in_phrase) > max_words:
                raise ValueError(
                    f"Cannot add '{phrase}' ({words_in_phrase} words) to a new"
                    f" sublist with {current_sublist_words} words"
                )

        # Add the current phrase to the sublist
        current_sublist.append(phrase)
        current_sublist_words  = words_in_phrase

    # At the end of the loop, add the working sublist to the output
    output_sublists.append(current_sublist)

    return output_sublists


print(sublists_with_max_words(qq))
 

Ответ №2:

Мой похож на ваш, и алгоритм в основном тот же, но я считаю, что это должно работать немного быстрее:

 def fn(lst, n):
    word_count = 0
    res = []
    temp_lst = []

    for item in lst:
        len_current_item = len(item.split())
        word_count  = len_current_item

        if word_count < n:
            temp_lst.append(item)

        else:
            res.append(temp_lst)
            last_item = res[-1][-1]
            temp_lst = [last_item, item]
            word_count = len_current_item   len(last_item.split())

    res.append(temp_lst)

    # Checking for last item's lenght as Phydeaux pointed out in comments.
    if word_count > n:
        res.append([temp_lst.pop()])

    return res
 

выход :

 ['blended e learning forumin planning', 'difficulties of learning as forigen language', 'difficulties of grammar']
['difficulties of grammar', 'students difficulties in grammar', 'difficulties of english grammar']
 

Я старался избегать копирования и очистки, а также немного небольших изменений.

Комментарии:

1. Это похоже на то, что я придумал, но, к вашему сведению word_count = len(item.split()) , не учитывает слова в res[-1][-1]

2. Также стоит учитывать, что происходит, когда есть два последовательных элемента, в которых >8 слов — в этом случае невозможно включить предыдущий элемент в новый список и не нарушить ограничение на количество слов. Не уверен, что ОП хочет, чтобы это произошло в этом случае

3. @Phydeaux Не могли бы вы подробнее рассказать о своем первом комментарии ? Вы указываете на последнюю итерацию, которая res[-1][-1] не учитывается?

4. Вы задаете количество слов только для текущего элемента, но в подсписке есть два элемента

5. @Phydeaux Спасибо, ты прав, это уже должно быть исправлено.

Ответ №3:

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

Во-первых, создайте диктат сопоставления:

 qq_map = {q: len(" ".join(qq[:n 1]).split()) for n, q in enumerate(qq)}

# {'blended e learning forumin planning': 5, 'difficulties of learning as forigen language': 11,
# 'difficulties of grammar': 14, 'students difficulties in grammar': 18,
# 'difficulties of english grammar': 22}
 

Затем вы создаете сгруппированный список с информацией о сопоставлении:

 qq = [[q for q in qq if qq_map[q] in range(i*16, (i 1)*16)] /
     for i in range(-(-qq_map[qq[-1]] // 16))]

# [['blended e learning forumin planning', 'difficulties of learning as forigen language', 'difficulties of grammar'], 
# ['students difficulties in grammar', 'difficulties of english grammar']]
 

Примечание: -(-qq_map[qq[-1]] // 16) является эквивалентом math.ceil(qq[-1] / 16) . Вы можете
заменить его, если хотите получить более краткое и менее «арифметическое»
выражение.

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

 qq = [[qq[i-1][-1]]   qq[i] if i != 0 else qq[i] for i in range(len(qq))]

# [['blended e learning forumin planning', 'difficulties of learning as forigen language', 'difficulties of grammar'], 
# ['difficulties of grammar', 'students difficulties in grammar', 'difficulties of english grammar']]