Фильтрация списка арабских предложений на основе языкового теста: почему так медленно? - PullRequest
2 голосов
/ 02 мая 2019

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

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

1 Ответ

1 голос
/ 02 мая 2019

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

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

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

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

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

...