12

Pythonでは、nがkの倍数である場合(IOW、 )、 n個の長いリストをkサイズのチャンクに分割するのは簡単です。これが私のお気に入りのアプローチです(ドキュメントから直接):n % k == 0

>>> k = 3
>>> n = 5 * k
>>> x = range(k * 5)
>>> zip(*[iter(x)] * k)
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)]

(トリックは、によって返される、同じイテレータへのk個の参照[iter(x)] * kのリストを生成することです。次に、イテレータのk個のコピーのそれぞれを1回だけ呼び出すことによって、各チャンクを生成します。「イテレータのリストではなく、イテレータ。)iter(x)zip*[iter(x)] * kzip

このイディオムで私が目にする主な欠点は、nがkの倍数(IOW、n % k > 0)でない場合、残りのエントリが省略されることです。例えば:

>>> zip(*[iter(x)] * (k + 1))
[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)]

入力するのが少し長く、上記の場合と同じ結果を生成し、次の場合n % k == 0により許容可能な動作をする代替イディオムがありn % k > 0ます。

>>> map(None, *[iter(x)] * k)
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)]
>>> map(None, *[iter(x)] * (k + 1))
[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11), (12, 13, 14, None)]

少なくとも、ここでは残りのエントリは保持されますが、最後のチャンクには。が埋め込まれNoneます。パディングに別の値が必要な場合はitertools.izip_longest、問題を解決します。

ただし、目的のソリューションが、最後のチャンクがパディングされないままになっているソリューションであるとします。

[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11), (12, 13, 14)]

この結果を生成するためにイディオムを変更する簡単な方法はありますか?map(None, *[iter(x)]*k)

(確かに、関数を記述してこの問題を解決することは難しくありません(たとえば、リストを均等なサイズのチャンクに分割するにはどうすればよいですか?またはしたがって、この質問のより正確なタイトルは「イディオムをどのように救うか」ですmap(None, *[iter(x)]*k)が、多くの読者を困惑させると思います。)

私は、リストを均等なサイズのチャンクに分割するのがいかに簡単であるか、そして2つの問題が同等の複雑さのように見えても、不要なパディングを取り除くのがいかに難しいか(比較して! )に感銘を受けました。

4

4 に答える 4

15
[x[i:i+k] for i in range(0,n,k)]
于 2011-08-10T03:01:17.157 に答える
3
sentinal = object()
split = ( 
    (v for v in r if v is not sentinal) for r in
    izip_longest(*[iter(x)]*n, fillvalue=sentinal))

もちろん、より良いイディオムは、同じことを行うものよりも読みやすい関数を呼び出すことです。

于 2011-08-10T03:01:19.263 に答える
3

IPythonのソースから:

def chop(seq,size):
    """Chop a sequence into chunks of the given size."""
    chunk = lambda i: seq[i:i+size]
    return map(chunk,xrange(0,len(seq),size))

シーケンスが均等に分割できない場合、返される最後のリストの要素数は少なくなりchunkます。基本的に、スティックの短い方の端を取得しますが、文句はありません。

>>> chop(range(12),3)
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
>>> chop(range(12),4)
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
>>> chop(range(12),5)
[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11]]
>>> chop(range(12),6)
[[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11]]
于 2011-08-10T08:05:31.057 に答える
1

これはどうですか?これは別のイディオムですが、目的の結果が得られます。

[x[i:i+k] for i in range(0,len(x),k)] #=> [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14]]
[x[i:i+k] for i in range(0,len(x),k)] #=> [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14]]

または、本当にタプルが必要な場合は、tuple(x[i:i+k])の代わりにを使用してくださいx[i:i+k]

于 2011-08-10T03:05:13.067 に答える