2013-02-09 37 views
10

私はグーグルでテストしましたが、これは私の知恵の終わりです。私は類似性によってグループ化する必要がある数字のリストを持っています。たとえば、[1,6,9,100,102,105,109,134,139]のリストでは、169がリストに入れられ、100,102,105、および109がリストに入れられます。リストと134と139.私は数学でひどいです、私は試してみましたが、私はそれを動作させることはできません。可能な限り明示的にするために、私は10の値の範囲内にある数値をお互いにグループ化したいと考えています。誰も助けることができますか?ありがとう。Pythonの数値のグループ化/クラスタリング

+4

「類似性」をより正確に定義する必要があります。数百、数十桁も同じですか? –

+0

つまり、お互いの値が10(またはそれ以上)の数字です。申し訳ありませんが、これを可能な限り明示的に表示しようとしました。 –

+1

可能なグループが重なる場合はどうなりますか? – millimoose

答えて

21

cluster analysisにはさまざまな方法があります。一つの簡単なアプローチは、連続したデータ要素間のギャップの大きさを見ることである。

def cluster(data, maxgap): 
    '''Arrange data into groups where successive elements 
     differ by no more than *maxgap* 

     >>> cluster([1, 6, 9, 100, 102, 105, 109, 134, 139], maxgap=10) 
     [[1, 6, 9], [100, 102, 105, 109], [134, 139]] 

     >>> cluster([1, 6, 9, 99, 100, 102, 105, 134, 139, 141], maxgap=10) 
     [[1, 6, 9], [99, 100, 102, 105], [134, 139, 141]] 

    ''' 
    data.sort() 
    groups = [[data[0]]] 
    for x in data[1:]: 
     if abs(x - groups[-1][-1]) <= maxgap: 
      groups[-1].append(x) 
     else: 
      groups.append([x]) 
    return groups 

if __name__ == '__main__': 
    import doctest 
    print(doctest.testmod()) 
+0

そして私の解決策があります。どうもありがとうございます。私はこのコードのすべての特徴を勉強します。 –

3

これはグループ見つける:NUMSが実際にあなたのサンプルが示すようにソートされていない場合は、あなたが最初にそれをソートする必要がありますことを

nums = [1, 6, 9, 100, 102, 105, 109, 134, 139] 
for k, g in itertools.groupby(nums, key=lambda n: n//10): 
    print k, list(g) 

0 [1, 6, 9] 
10 [100, 102, 105, 109] 
13 [134, 139] 

注意を。

+5

このアプローチについて私が気に入らないのは、 '' [1,6,9,99,100,134,139] '' * 99 *と* 100 *を異なるグループにグループ化するということだけです。 1つのクラスタの開始位置と終了位置を決定するために、連続するデータポイント間の差異を計算する方がよいでしょう。 –

+1

残念ながら、私はこのコードを試したときに起こったことです。ほぼ完璧です。 –

+1

はい、私はそれを書いたときに特定されていませんでした。 –

2

まず、あなたは簡単に隣接する項目のペアのシーケンスに任意のシーケンスを変換することができます。ちょうどそれにティーをかけ、それを前方にシフトし、シフトされていないコピーとシフトされていないコピーをジップします。唯一のトリックは、このケースでは、我々は要素の各ペアが、各要素のペアをいないしたいので、あなたが、(<something>, 1)または(139, <something>)のいずれかで開始する必要があるということです。

def pairify(it): 
    it0, it1 = itertools.tee(it, 2) 
    first = next(it0) 
    return zip(itertools.chain([first, first], it0), it1) 

(これはする最も簡単な方法ではありませんそれを書くが、私は、これはitertoolsに慣れていない人に最も読みやすいです方法かもしれないと思う。)

>>> a = [1, 6, 9, 100, 102, 105, 109, 134, 139] 
>>> list(pairify(a)) 
[(1, 1), (1, 6), (6, 9), (9, 100), (100, 102), (102, 105), (105, 109), (109, 134), (134, 139)] 

その後、ネッドBatchelderのキーのもう少し複雑なバージョンで、あなただけのgroupbyを使用することができます。

しかし、私はこの場合、同じことをする明示的なジェネレータよりも複雑になると思います。

def cluster(sequence, maxgap): 
    batch = [] 
    for prev, val in pairify(sequence): 
     if val - prev >= maxgap: 
      yield batch 
      batch = [] 
     else: 
      batch.append(val) 
    if batch: 
     yield batch 
関連する問題