2016-09-08 2 views
4

У меня есть два списка, A и B. Я хочу создать третий список, который равен 1, если соответствующая запись в A имеет запись в списке B на конец строки и 0 в противном случае.Использование представления списка для данных меток, которое является общим для двух списков

A = ['Mary Sue', 'John Doe', 'Alice Stella', 'James May', 'Susie May'] 
B = ['Smith', 'Stirling', 'Doe'] 

Я хочу, чтобы список понимание, что даст результата

[0, 1, 0, 0, 0] 

Имейте в виду, что это частный случай более общей проблемы. Элементы в A могут иметь произвольное пустое пространство и содержать в них произвольное количество слов. Аналогично, элементы в B могут иметь произвольное количество слов. Например

A = [' Tom Barry Stirling Adam', 'Maddox Smith', 'George Washington Howard Smith'] 
B = ['Washington Howard Smith', 'Stirling Adam'] 

должен вернуть

[1, 0, 1] 

До сих пор я следующее

[1 if y.endswith(x) else 0 for x in B for y in A] 

Однако длина возвращаемого списка не размер, что я хочу, потому что это дает 0 или 1 для каждой комбинации элементов A [i], B [j]. Я не интересуюсь решениями, используемыми для циклов, мне нужно понимание списка для скорости.

+0

Будет ли всегда пропущено первое имя и всегда будут подстроены подстроки? –

+0

Нет, могут быть случаи, когда A и B могут содержать одно и то же имя. – deltap

+0

Так что буквально возможны варианты? –

ответ

2

гораздо более быстрый способ это передать кортеж EndsWith:

In [8]: A = ['Mary Sue', 'John Doe', 'Alice Stella', 'James May', 'Susie May'] 

In [9]: B = ['Smith', 'Stirling', 'Doe']    

In [10]: A *= 1000 

In [11]: %%timeit               
t = tuple(B) 
[int(s.endswith(t)) for s in A] 
    ....: 
100 loops, best of 3: 5.02 ms per loop 

In [12]: timeit [int(any(full.endswith(last) for last in B)) for full in A] 
100 loops, best of 3: 21.3 ms per loop 

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

Вы можете увидеть, используя больший набор слов, насколько быстрее это особенно если матчи немногочисленны:

In [2]: from random import sample 

In [6]: A = [s.strip() for s in open("/usr/share/dict/american-english")][:20000] 

In [7]: B = sample([s.strip() for s in open("/usr/share/dict/british-english")], 2000) 

In [8]: %%timeit                  
t = tuple(B) 
[int(s.endswith(t)) for s in A] 
    ...: 

1 loop, best of 3: 2.16 s per loop 
In [9]: timeit [int(any(full.endswith(last) for last in B)) for full in A]    
1 loop, best of 3: 26.6 s per loop 

Вы сказали, что вы не хотите петли, а как списки растут сортировка может быть лучший вариант, то разрез`ать найти любые совпавшие строки с лог-н поиска реверсивной логики:

from bisect import bisect_left 


def compress(l1, l2): 
    srt1 = sorted(s[::-1] for s in l2) 
    hi = len(l2) 
    for ele in l1: 
     rev = ele[::-1] 
     ind = bisect_left(srt1, rev, hi=hi) 
print(list(compress(A, B))) 

среда является O (N журнал N), в отличие от квадратичного подхода проверки каждой подстроки.

+0

Ничего себе, гораздо быстрее! Даже факторинг в преобразовании кортежа. – deltap

+1

huh, я не знал, что 'endswith' работал с кортежем. круто –

0
>>> [[0, 1][name.split()[-1] in set(B)] for name in A] 
[0, 1, 0, 0, 0] 

Редактировать: Для более точного контроля над проверкой.

str.split может принимать параметр, который является максимальным расщеплением. например:

>>> 'Mary Pat Sue'.split(maxsplit=1) 
['Mary', 'Pat Sue'] 

Тогда мы могли бы сравнить, чтобы увидеть, если любое имя в B во втором значении раскола:

>>> B = ['Pat', 'Sue'] 
>>> any(name in 'Pat Sue' for name in B) 
True 

Так в целом:

>>> [[0, 1][any(surname in fullname.split(maxsplit=1)[-1] for surname in B)] 
    for fullname in A] 
+0

Это небольшая абстракция немного более сложной проблемы. Я не могу использовать split так, потому что список A может иметь что-то вроде «Mary Pat Sue», который должен быть помечен как 1, если список B содержит «Pat Sue» или «Sue». – deltap

2

Ваше состояние необходимо провести список B. Ваше предлагаемое решение будет генерировать 0 или 1 для каждой пары элементов (A, B).

[1 if any(full.endswith(last) for last in B) else 0 for full in A] 

Но вы также можете воспользоваться bool для int преобразования

[int(any(full.endswith(last) for last in B)) for full in A] 

Вы можете сэкономить время, используя set и оператор in, а также:

B = {'Smith', 'Stirling', 'Doe'} # set for a more efficient `in` 
[int(full.split()[-1] in B) for full in A] 
+0

Ваши первые два решения отлично работают. Третий использует split и, как я объяснил в комментариях к другим решениям, не будет работать для меня, потому что он не будет обрабатывать случаи, когда список A может иметь что-то вроде «Mary Pat Sue», который должен быть помечен как 1, если список B содержит «Пэт Сью» или «Сью». – deltap

+0

@deltap вы должны обновить свой вопрос, чтобы отразить это, а не добавлять комментарии, или вы продолжите получать аналогичные ответы. –

+0

Хороший момент, сделано! – deltap

0

[1 if a.split(' ')[1] in B else 0 for a in A]

+0

Это небольшая абстракция немного более сложной проблемы. Я не могу использовать split так, потому что список A может иметь что-то вроде «Mary Pat Sue», который должен быть помечен как 1, если список B содержит «Pat Sue» или «Sue». – deltap

+0

[** 'str.split' **] (https://docs.python.org/2/library/stdtypes.html#str.split) по умолчанию разделяет пробелы. –

0

Насколько велик Б в реальной жизни? Вы можете превратить его в регулярное выражение.

A = ['Mary Sue', 'John Doe', 'Alice Stella', 'James May', 'Susie May'] 
B = ['Smith', 'Stirling', 'Doe'] 

очередь B в ".*(?:Smith|Stirling|Doe)$" затем компилировать регулярное выражение

import re 
end_with_b = re.compile(".*(?:{})$".format("|".join(B)) 

a_matches = [1 if ends_with_b.match(a) else 0 for a in A] 

Или создать свою собственную функцию фильтра

def my_filter(a): 
    return 1 if any(a.endswith(b) for b in B) else 0 

a_matches = [my_filter(a) for a in A] 
+0

B может быть довольно большим. На данный момент это ~ 200 000 записей, но в принципе это может быть больше. A также большой, имея миллионы записей. – deltap

Смежные вопросы