2016-09-08 1 views
4

2つのリストAとBがあります。Aの対応するエントリがリストBにある場合は1です。文字列の末尾には0が入ります。リスト内包表記を使用して2つのリストに共通するデータにラベルを付ける

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

私は、これは、より一般的な問題の特定の場合であることに注意してください結果に

[0, 1, 0, 0, 0] 

を与えるリストの内包をしたいです。 Aの要素は任意の空白を持つことができ、任意の数の単語を含むことができます。同様に、Bの要素は任意の数の単語を持つことができます。返されるリストの長さは、それが0を与えるので、私はディメンションではありませんしかし、例えば

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

は、これまでのところ、私は

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

を以下している

[1, 0, 1] 

を返す必要がありますA [i]、B [j]要素の全ての組み合わせに対して1である。 forループを使用したソリューションに興味はありません。スピードのためにリストの理解が必要です。

+0

最初の名前は常にスキップされ、常に完全な部分文字列に一致しますか? –

+0

いいえ、AとBが全く同じ名前を含む場合があります。 – deltap

+0

文字通り任意のバリエーションが可能ですか? –

答えて

2

はRegexしコンパイルします。

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に要素ごと1つの関数呼び出しを行います。あなたは試合が疎であれば、それは特にですどれだけ速いの単語の大規模なセットを使用して見ることができます

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 

あなたがループを望んでいないが、リストがソート育つようであるかもしれないと述べましたより良いオプションは、ログを持つ任意のマッチした文字列を検索するn個の論理を反転させる検索する二分:すべての部分文字列をチェックし、二次的なアプローチとは対照的に、

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))) 

ランタイムは(N Nをログ)Oです。

+0

すごく早く!タプル変換を因数分解することさえできます。 – deltap

+1

ねえ、私はタプルで働いた 'endswith 'を知らなかった。クール –

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

編集:チェックを細かく制御するため。

str.splitは、最大分割するパラメータを取ることができます。例えば:

>>> 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

これはやや複雑な問題の少し抽象です。 Aリストには「Pat Sue」や「Sue」が含まれていると1とラベル付けされるべき「Mary Pat Sue」のようなものがあるかもしれないので、私は分割を使用できません。 – deltap

2

あなたの状態が保持する必要がある

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

その後、我々はBで任意の名前は、分割の第二の値であるかどうかを確認するために比較することができBリスト提案されたソリューションは、(A、B)要素のすべてのペアに対して0または1を生成します。

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

しかし、あなたはまた、あなたが同様にsetin演算子を使用していくつかの時間を節約することができint変換

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

boolを利用できます。

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

最初の2つのソリューションは素晴らしいです。 3つ目はsplitを使用し、他のソリューションに関するコメントで説明したように、Aリストには「Mary Pat Sue」のようなものがあり、Bリストの場合は1と表示されるケースを処理できないためです'Pat Sue'または 'Sue'が含まれています。 – deltap

+0

@deltapコメントを追加する代わりにこれを反映するために質問を更新する必要があります。そうしないと、似たような回答が得られます。 –

+0

良い点、完了! – deltap

0

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

+0

これはやや複雑な問題のわずかな抽象です。 Aリストには「Pat Sue」や「Sue」が含まれていると1とラベル付けされるべき「Mary Pat Sue」のようなものがあるかもしれないので、私は分割を使用できません。 – deltap

+0

[** 'str.split' **](https://docs.python.org/2/library/stdtypes.html#str.split)は、デフォルトで空白を分割します。 –

0

実生活でBの大きさはどれくらいですか?正規表現にすることができます。".*(?:Smith|Stirling|Doe)$"

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

ターンBその後、はるかに高速な方法は、 endswith にタプルを渡すことです

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

関連する問題