1 Вопрос: Фильтрация списка арабских предложений на основе языкового теста: почему так медленно?

вопрос создан в Thu, May 2, 2019 12:00 AM

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

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

Есть ли что-то, что я могу сделать, чтобы сделать его более эффективным?

Спасибо!

def test_lang(string):
    """Takes a string and determines if it is written in Arabic 
    characters or foreign, by testing whether the first character 
    has a case attribute. This is intended for Arabic texts that  
    may have English or French words added. If it encounters another 
    case-less language (Chinese for instance), it will falsely 
    identify it as Arabic."""

    if not string or not string.isalpha():
        return None
    char = string[0]
    if char.isalpha() and not (char.islower() or char.isupper()):
        lang = 'AR'
    else:
        lang = 'FW'
    return lang

...

# remove sentences that are in English or French - THIS IS SLOW (takes a few mins)
for sent in sents:
    if sent and test_lang(sent[0]) != 'AR':
        sents.remove(sent)

# remove clearly MSA sentences -- THIS IS FAST (takes a few seconds)
msa_features = ['ليس','لست','ليست','ليسوا','الذي','الذين','التي','ماذا', 'عن']
p = re.compile('|'.join(msa_features))
for sent in sents:
    if re.search(p, sent):
        sents.remove(sent)
    
2
1 ответ                              1                         

list.remove очень медленный для этой цели - он ищет во всем списке заданное значение каждый раз, а затем удаляет его. Он должен эффективно перебирать весь список для каждого удаляемого элемента, вызывая квадратичное время выполнения.

Лучшим решением здесь было бы следующее выражение списка:

sents = [
    sent for sent in sents
    if test_lang(sent[0]) == 'AR' and not re.search(p, sent)
]

Это фильтрует список по линейному времени.

(Я полагаю, что первый фильтр должен работать с очень длинным списком и отбрасывать большую его часть? В то время как второй получает гораздо меньший список и не должен удалять слишком много? Это объясняет, почему первый один намного медленнее.)

    
1
2019-05-02 15: 05: 27Z
  1. Вау, это так намного быстрее. По какой-то причине он удаляет намного больше, чем мой оригинальный код, но это нормально, потому что здесь я все равно пытаюсь получить подмножество корпуса. Большое вам спасибо!
    2019-05-02 15: 51: 13Z
  2. Интересно, что это удаляет больше ... если вы хотите найти причину, вы можете разделить два условия фильтра на два отдельных оператора и сравнить результаты с оригинальный код.
    2019-05-03 07: 42: 29Z
источник размещен Вот