Как ускорить расширение списка с помощью итератора карты

У меня есть текстовые данные в столбце панд. Обычно каждый документ является частью значения столбца. Каждый документ состоит из нескольких предложений.

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

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

def text_to_words(x):
    """ This function converts sentences in a text to a list of words

    """
    nlp=spacy.load('en')
    txt_to_words= [str(doc).replace(".","").split(" ") for doc in nlp(x).sents]
    return txt_to_words

Тогда я сделал это:

%%time
txt_to_words=map(text_to_words,pandas_df.log_text_cleaned)

Это было сделано за 70 микросекунд, и я получил итератор картографа.

Теперь, если я хочу добавить каждый список списка слов каждого документа как новое значение нового столбца в том же фрейме данных pandas.

Я могу просто сделать это:

txt_to_words=[*map(text_to_words,pandas_df.log_text_cleaned)]

Что расширит итератор карты и сохранит его в txt_to_words как список списка слов.

Но этот процесс идет очень медленно.

Я даже пробовал перебирать объект карты:

txt_to_words=map(text_to_words,pandas_df.log_text_cleaned)


txt_to_words_list=[]
for sent in txt_to_words: 
    txt_to_words_list.append(sent)

Но это аналогично медленно.

извлечение вывода из объекта сопоставления происходит очень медленно. И у меня только 67 КБ документов в столбце фрейма данных pandas.

Есть ли способ ускорить это?

Спасибо

«Это было сделано за 70 микросекунд, и я получил итератор картографа» - работа не выполнена. В этом суть map, возвращающего итератор: он не выполняет свою работу, пока вы не зациклите его. Вы не ускорили работу. Вы только что сказали Python откладывать дела на потом.

user2357112 supports Monica 27.03.2018 01:50

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

abarnert 27.03.2018 01:50

При этом: вызов list(…) вместо [*…] или оператора for будет значительно быстрее ... просто он ускорит ту часть, которая не имеет значения, сокращая микросекунды вашего общего процесса.

abarnert 27.03.2018 01:51

даже использование list () работает очень медленно. В этом нет большой разницы и в использовании цикла * или for. Есть ли другие предложения, которые могут ускорить это? Есть ли способ отправить каждое предложение по одной строке в текстовый файл, чтобы все предложения всех документов просто записывались в текстовый файл на диске одно под другим? Я пробовал сделать это, но он стирает все предыдущие предложения и сохраняет только последние предложения документов. Файл еще не создан, поэтому он должен быть в режиме 'w', чтобы он мог записать туда первый документ.

Baktaawar 27.03.2018 01:54

@Baktaawar Да. Как я только что вам объяснил, это потому, что вы оптимизируете не то. Составление списка занимает микросекунды, реальная работа занимает секунды. Так что ускорение построения списка не окажет заметного влияния.

abarnert 27.03.2018 01:56

Если вы мне не верите, попробуйте собрать collections.deque(map…, maxlen=0). Это самый быстрый способ использования итератора - ему не нужно выделять память или хранить что-либо. И вы увидите, что это по-прежнему не имеет видимого значения.

abarnert 27.03.2018 01:57

правильно, я понял. Так что в основном это не поможет в памяти. Можно как-то запихнуть на диск по txt файлу один под другим?

Baktaawar 27.03.2018 01:59

Доступ к диску не ускорит работу.

user2357112 supports Monica 27.03.2018 02:01

Я не совсем уверен, что находится в pandas_df.log_text_cleaned или что возвращает nlp(x).sents, но это те вещи, на которые вам нужно обратить внимание. См. Мой ответ, чтобы начать работу.

abarnert 27.03.2018 02:05

@abarnert: На самом деле, [*...] имеет точно такую ​​же стоимость за единицу, что и list(...), и немного меньшую фиксированную стоимость. Оба они выполняют всю реальную работу на уровне C на CPython, а версия на основе синтаксиса имеет специальную поддержку байт-кода, которая позволяет избежать общих накладных расходов на диспетчеризацию вызовов list(...) (экономит колоссальные 100-150 нс для выполнения [*...]). Тем не менее, все остальное, что вы сказали, верно. Если для производства каждого элемента требуется 10 РС, не имеет значения, если вы сэкономите 10-50 нс на стоимости каждого элемента, чтобы добавить его в list. (*...,) работает медленнее, чем tuple(...) (создает list, а затем копирует на tuple).

ShadowRanger 27.03.2018 02:09

@ShadowRanger У него более высокая фиксированная стоимость в быстром тесте %timeit с 3.6 на Linux и 3.7alpha на Mac - 137us против 129us (против 106us для deque(maxlen=0), хотя эта разница становится шире для гораздо больших итераторов, а другой - нет) т). Возможно, все было бы по-другому с большим количеством крошечных списков (где действительно имеет значение стоимость поиска глобального и вызова функции) вместо меньшего количества приличных списков.

abarnert 27.03.2018 02:13
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
11
52
1

Ответы 1

Прямой ответ на ваш вопрос заключается в том, что самый быстрый способ преобразовать итератор в список, вероятно, - вызвать на нем list, хотя это может зависеть от размера ваших списков.

Однако это не имеет значения, за исключением незаметной, едва поддающейся измерению степени.

Разница между list(m), [*m] или даже явным выражением for составляет максимум микросекунды, но ваш код занимает секунды. Фактически, вы даже можете исключить почти всю работу, выполняемую list, используя collections.deque(m, maxlen=0) (который просто отбрасывает все значения, ничего не выделяя и не сохраняя), и вы по-прежнему не увидите разницы.


Ваша настоящая проблема в том, что работа над каждым элементом выполняется медленно.

Вызов map на самом деле не работает. Все, что он делает, это создает ленивый итератор, который настраивает работу, которую нужно выполнить позже. Когда позже? Когда вы конвертируете итератор в список (или используете его другим способом).

Итак, именно эту функцию text_to_words вам нужно ускорить.

И есть по крайней мере один очевидный кандидат на то, как это сделать:

def text_to_words(x):
    """ This function converts sentences in a text to a list of words

    """
    nlp=spacy.load('en')
    txt_to_words= [str(doc).replace(".","").split(" ") for doc in nlp(x).sents]
    return txt_to_words

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

Если вы пытались ускорить процесс, сделав его локальной переменной, а не глобальной (что, вероятно, не имеет значения, но может), это не способ сделать это; это:

nlp=spacy.load('en')

def text_to_words(x, *. _nlp=nlp):
    """ This function converts sentences in a text to a list of words

    """
    txt_to_words= [str(doc).replace(".","").split(" ") for doc in _nlp(x).sents]
    return txt_to_words

Ну, у меня нет этой нагрузки nlp в моей реальной функции. Его снаружи. Я добавил его сюда, чтобы понять, что такое переменная nlp. Так что вышесказанное не помогает, так как это у меня уже есть снаружи.

Baktaawar 27.03.2018 06:06

Другие вопросы по теме