2017-04-06 4 views
3

私は、Pythonに新たなんだと私はグループがそのようなNoneシグナル継続アイテムとアイテムを一覧表示機能の書き込みをしようとしている:私の実際のデータは、はるかに複雑な項目がありますが、私はしましたリスト内の「継続」アイテムをグループ化します。 itertoolsのgroupbyキーの機能が悪いのですか?

>>> g([1, None, 1, 1, None, None, 1]) 
[[1, None], [1], [1, None, None], [1]] 

をこの質問のためにコアに単純化された事柄。

これは、これまでのところ私のソリューションです:

import itertools 

# input 
x = [1, None, 1, 1, None, None, 1] 

# desired output from g(x) 
y = [[1, None], [1], [1, None, None], [1]] 


def f(x): 
    if x is None: 
     f.lastx = x 
    else: 
     if x != f.lastx: 
      f.counter += 1 
    return f.counter 


def g(x): 
    f.lastx = None 
    f.counter = 0 
    z = [list(g) for _, g in itertools.groupby(x, f)] 
    return z 


assert y == g(x) 

は、これは動作しますが、私はそれが非常に醜いと知っています。

これを行うにはより良い(そしてもっとPythonの)方法がありますか?例えば。ステートフルなキー機能はありません。

答えて

2

あなたはitertools.groupbyitertools.accumulateを組み合わせることができます

>>> dat = [1, None, 1, 1, None, None, 1] 
>>> it = iter(dat) 
>>> acc = accumulate(x is not None for x in dat) 
>>> [[next(it) for _ in g] for _, g in groupby(acc)] 
[[1, None], [1], [1, None, None], [1]] 

accumulateは私たちにすべての新しいグループの開始時にintlike値を増やす与えるので、これは動作します:あなたがしたい場合

>>> list(accumulate(x is not None for x in dat)) 
[True, 1, 2, 3, 3, 3, 4] 

をストリームを処理できるように、イテレータはteeです。メモリ使用量の最大増加は、1つのグループのサイズのオーダーだけです。

def cgroup(source): 
    it, it2 = tee(iter(source), 2) 
    acc = accumulate(x is not None for x in it) 
    for _,g in groupby(acc): 
     yield [next(it2) for _ in g] 

これはまだ

>>> list(cgroup([1, None, 1, 1, None, None, 1])) 
[[1, None], [1], [1, None, None], [1]] 

を与えますが、でも、無限の源で動作します:

>>> stream = chain.from_iterable(repeat([1, 1, None])) 
>>> list(islice(cgroup(stream), 10)) 
[[1], [1, None], [1], [1, None], [1], [1, None], [1], [1, None], [1], [1, None]] 
+0

うわー、それは密なコードです。 :)私はそれがどのように動作するのか理解するのは少しはかりましたが、私はそれが好きで、このアプローチがどのようにかなりフレキシブルであるかを見ることができます。 –

+0

実際、私はちょうど何かに気付いた...このアプローチは、入力データに対して2回のパスが必要です。データがストリーミングされている場合(大きな問題であるため)、この方法は機能しません。 :( –

+0

@BrianG:あなたは* list *アイテムをグループ化していると言ってはいけないでしょう。;-)しかしストリームを扱うのも簡単です。 – DSM

1

それは、サードパーティ製の拡張機能(iteration_utilities.split)といくつかの工夫が必要であるので、それは完璧ではないのですが、所望の出力を生成する:

>>> from iteration_utilities import split, is_not_None 

>>> lst = [1, None, 1, 1, None, None, 1] 

>>> list(split(lst, is_not_None, keep_after=True))[1:] 
[[1, None], [1], [1, None, None], [1]] 

この方法で最初の要素を破棄する必要があります(したがって[1:])。そうしないと、結果が空のサブリストで開始されるためです。

+0

私にこれを知らせてくれてありがとう。私はいくつかのタイプの分割がグループバイよりも適切かもしれないと考えていました。私が完全に機能し、もっと多くのライブラリを使用したいのであれば、それは意味をなさないかもしれませんが、今のところ、この1つの関数だけに依存性を追加することはありません。 –

関連する問題