2016-06-20 11 views
1

私は〜100,000の異なる数のセットを持っていると言います。いくつかは連続的であり、いくつかは連続的ではない。Pythonの数字セットから「スライス表記」スタイルリストを作成する

問題を示すため、これらの数値の小さいサブセットはかもしれない:

(A){1,2,3,4,5,6,7,8,9,11,13、 15,45,46,47,3467}

このサブセットを書き込む効率的な方法は次の通りである:

(b)は、1:9:15:1,11 2,45: 47:1,3467

これは実質的にpythonとmatlabのスライス表記の拡張バージョンです。

私の質問は、前者のリストから、Pythonの後ろの表記法で効率的にリストを取得するにはどうすればいいですか?

つまり、(a)で与えられているように、(b)はどのように効率的にPythonで入手できますか?

+2

スタートを例えばを見て、 http://stackoverflow.com/q/3149440/3001761 – jonrsharpe

+0

少なくとも適切なトラックに私を置いている有用なスレッド。しかし、私が言うことができる限り、それは私に上記のようなストライド情報をもたらさない。 – Kobs

+1

@ Kobyeスライス - >配列からの答えを受け入れました、あなたは他の方法に行きたいと思ったのですか? –

答えて

1

私はそれを得たが、次のコードは非常にthoroughlではなかったと思いますyがテストされ、バグが含まれる可能性があります。

基本的にget_partial_slicesは(ソート)セットの次の数は、それが.end()編で、次のスライスが開始されたスライスに.fit()をしていない場合、partial_sliceオブジェクトを作成しようとします。

スライスに1つのアイテム(または2つのアイテムと2つのアイテム、step!=1)がある場合は、スライスではなく別の番号として表示されます(yield from current.end()が必要なため、スライスを終了すると1つのスライス)

class partial_slice: 
    """heavily relied on by get_partial_slices 
This attempts to create a slice from repeatedly adding numbers 
once a number that doesn't fit the slice is found use .end() 
to generate either the slice or the individual numbers""" 
    def __init__(self, n): 
     self.start = n 
     self.stop = None 
     self.step = None 
    def fit(self,n): 
     "returns True if n fits as the next element of the slice (or False if it does not" 
     if self.step is None: 
      return True #always take the second element into consideration 
     elif self.stop == n: 
      return True #n fits perfectly with current stop value 
     else: 
      return False 

    def add(self, n): 
     """adds a number to the end of the slice, 
    will raise a ValueError if the number doesn't fit""" 
     if not self.fit(n): 
      raise ValueError("{} does not fit into the slice".format(n)) 
     if self.step is None: 
      self.step = n - self.start 
     self.stop = n+self.step 

    def to_slice(self): 
     "return slice(self.start, self.stop, self.step)" 
     return slice(self.start, self.stop, self.step) 
    def end(self): 
     "generates at most 3 items, may split up small slices" 
     if self.step is None: 
      yield self.start 
      return 
     length = (self.stop - self.start)//self.step 
     if length>2: 
      #always keep slices that contain more then 2 items 
      yield self.to_slice() 
      return 
     elif self.step==1 and length==2: 
      yield self.to_slice() 
      return 
     else: 
      yield self.start 
      yield self.stop - self.step 


def get_partial_slices(set_): 
    data = iter(sorted(set_)) 
    current = partial_slice(next(data)) 
    for n in data: 
     if current.fit(n): 
      current.add(n) 
     else: 
      yield from current.end() 
      current = partial_slice(n) 
    yield from current.end() 


test_case = {1,2,3,4,5,6,7,8,9,11,13,15,45,46,47,3467} 
result = tuple(get_partial_slices(test_case)) 

#slice_set_creator is from my other answer, 
#this will verify that the result was the same as the test case. 
assert test_case == slice_set_creator[result] 

def slice_formatter(obj): 
    if isinstance(obj,slice): 
     # the actual slice objects, like all indexing in python, doesn't include the stop value 
     # I added this part to modify it when printing but not when created because the slice 
     # objects can actually be used in code if you want (like with slice_set_creator) 
     inclusive_stop = obj.stop - obj.step 
     return "{0.start}:{stop}:{0.step}".format(obj, stop=inclusive_stop) 
    else: 
     return repr(obj) 

print(", ".join(map(slice_formatter,result))) 
+0

これは私が欲しいものです。 Python 2.7で動作させるには、current.end()のyieldをcurrent.end()のyield barに変更する必要がありました。現在、私は常に一歩進んでいますが、私はこの問題を自分でかなり簡単に解決できるはずです。このきちんとした解決策を考えてくれてありがとう。 – Kobs

+1

問題はありません。一つのステップが多すぎるのは、エンドポイントを含まないPythonスライスを使用しているからです。実際にスライスオブジェクトをそのまま残しておくと仮定して、 'slice_formatter'で補正しています。あなたは理論的にそれらを実際のコードで使用することができますか? (そうでなければ 'to_slice'を変更するだけです) –

1

免責事項:私は質問を誤解し、スライス表記からセットバージョンへの移行を考えていました。これはあなたの質問に実際には答えませんが、残しておく価値があると思いました。 numpy._rは同じ(または少なくとも非常に似たような)ことをしているようです。あなたのpython 3.5+を使用している場合PEP 3132が与える

まずオフノートがセットリテラルで*unpackingメソッドを使用するオプションです:

>>> {*range(1,9), *range(11,15,2), *range(45,47), 3467} 
{1, 2, 3, 4, 5, 6, 7, 8, 11, 3467, 13, 45, 46} 

__getitem__または__setitem__がオンに使用されたときにそれ以外の場合は表記11:15:2のみが使用されていますオブジェクトなので、あなたは自分のセットを生成するオブジェクトを設定する必要があります:

def slice_to_range(slice_obj): 
    assert isinstance(slice_obj, slice) 
    assert slice_obj.stop is not None, "cannot have stop of None" 
    start = slice_obj.start or 0 
    stop = slice_obj.stop 
    step = slice_obj.step or 1 
    return range(start,stop,step) 

class Slice_Set_Creator: 
    def __getitem__(self,item): 
     my_set = set() 
     for part in item: 
      if isinstance(part,slice): 
       my_set.update(slice_to_range(part)) 
      else: 
       my_set.add(part) 
     return my_set 

slice_set_creator = Slice_Set_Creator() 

desired_set = slice_set_creator[1:9:1,11:15:2,45:47:1,3467] 

>>> desired_set 
{1, 2, 3, 4, 5, 6, 7, 8, 11, 3467, 13, 45, 46} 
+1

ああ、私はあなたが他の道に行きたいと思っていたと思った.... –

+0

確かに、私は反対に行きたい!それにもかかわらず、反対の答えを提供するための時間をとっていただきありがとうございます。ある日、他のユーザーにとって便利なことがあります。 – Kobs

+1

ええ、それについて申し訳ありません、学んだ私はこの表記で範囲を作ることができ、興奮しました私は他のすべての失われたトラックのようなものを示すことができました(黒っぽい)スライスにセットから行くことは、 1つのためには、おそらく100000以上の数字を '並べ替え 'することを意味する順序で数字が必要になります。また、孤立した数字を決める方法と2つの数字のスライスを作る方法(あるいは少なくとも3つの数字が必要なのでしょうか?) '{2,4,6,7,8,9} 7:2,8:10:1] 'または' [2:5:2、7:10:1] 'など、 –

1

最も簡単な方法は、numpyのr_[]構文を使用することです。だからあなた例えば、それだけで次のようになります。

>>> from numpy import r_ 
>>> 
>>> a = r_[1:10, 11:17:2, 45:48, 3467] 

はPythonのスライスが最後の番号が含まれていないことに注意してください、そしてX:Y:1が暗示されます。また、このアプローチは、もう一つの洗練されたソリューションほど実動コードでは高速ではありませんが、インタラクティブな使用には適しています。

あなたは、これはあなたがしたい番号のnumpyの配列を与えることがわかります。

>>> print(a) 
[ 1 2 3 4 5 6 7 8 9 11 13 15 45 46 47 
3467] 
関連する問題