21

2 で割り切れるすべての数と 3 で割り切れるすべての数の数など、単純な統計を収集したい反復可能なエントリがあります。

私の最初の選択肢は、リストを一度だけ反復し、リストの展開を回避する (そして分割ループのリファクタリングを念頭に置く) ことですが、かなり肥大化したように見えます。

(代替 1)

r = xrange(1, 10)

twos = 0
threes = 0

for v in r:
  if v % 2 == 0:
    twos+=1
  if v % 3 == 0:
    threes+=1

print twos
print threes

これはかなり見栄えがしますが、式をリストに展開するという欠点があります。

(代替 2)

r = xrange(1, 10)

print len([1 for v in r if v % 2 == 0])
print len([1 for v in r if v % 3 == 0])

私が本当に欲しいのは、次のような関数のようなものです:

(代替 3)

def count(iterable):
  n = 0
  for i in iterable:
    n += 1
  return n

r = xrange(1, 10)

print count(1 for v in r if v % 2 == 0)
print count(1 for v in r if v % 3 == 0)

しかし、これは関数なしで実行できるものによく似ています。最終的なバリアントは次のとおりです。

(代替 4)

r = xrange(1, 10)

print sum(1 for v in r if v % 2 == 0)
print sum(1 for v in r if v % 3 == 0)

最小 (そして私の本ではおそらく最もエレガント) ですが、意図をうまく表現していないように感じます。

だから、あなたへの私の質問は次のとおりです。

これらのタイプの統計を収集するには、どの代替手段が最適ですか? より良いものがあれば、自由に独自の代替案を提供してください。

以下の混乱を解消するために:

  • 実際には、私のフィルター述語は、この単純なテストよりも複雑です。
  • 繰り返し処理するオブジェクトは、単なる数値よりも大きく複雑です
  • 私のフィルター関数はより異なっており、1 つの述語にパラメーター化するのが難しい
4

12 に答える 12

20

リストを複数回反復する必要があるのは、エレガントな私見ではありません。

おそらく、次のことができる関数を作成します。

twos, threes = countmatching(xrange(1,10),
                             lambda a: a % 2 == 0,
                             lambda a: a % 3 == 0)

出発点は次のようになります。

def countmatching(iterable, *predicates):
    v = [0] * len(predicates)
    for e in iterable:
        for i,p in enumerate(predicates):
            if p(e):
                v[i] += 1
    return tuple(v)

ところで、「itertools レシピ」には、alt4 と同じように行うためのレシピがあります。

def quantify(seq, pred=None):
    "Count how many times the predicate is true in the sequence"
    return sum(imap(pred, seq))
于 2008-10-01T11:09:14.667 に答える
7

代替 4! しかし、割り切れる数 (2 と 3) を含む引数を取る関数にコードをリファクタリングする必要があるかもしれません。そして、より良い関数名を持つことができます。

def methodName(divNumber, r):
  return sum(1 for v in r if v % divNumber == 0)


print methodName(2, xrange(1, 10))
print methodName(3, xrange(1, 10))
于 2008-10-01T11:04:46.737 に答える
4

機能を使用できますfilter

指定された関数が true と評価されるアイテムのみを含む新しいリストを生成するリスト (または厳密には iterable) をフィルタリングします。

r = xrange(1, 10)

def is_div_two(n):
    return n % 2 == 0

def is_div_three(n):
    return n % 3 == 0

print len(filter(is_div_two,r))
print len(filter(is_div_three,r))

これは、統計ロジックを関数に含めておくことができ、その意図filterがかなり明確になるため、優れています。

于 2008-10-01T10:51:36.457 に答える
3

あなたの(alt 4)の小さな変形を選択します:

def count(predicate, list):
    print sum(1 for x in list if predicate(x))

r = xrange(1, 10)

count(lambda x: x % 2 == 0, r)
count(lambda x: x % 3 == 0, r)
# ...

count の動作を変更したい場合は、その実装を 1 か所で変更してください。

注: 述語は複雑なので、おそらくラムダではなく関数で定義することをお勧めします。そのため、これらすべてをグローバル名前空間ではなくクラスに配置することをお勧めします。

于 2008-10-02T16:16:40.627 に答える
1

真のブール値は単位整数に強制変換され、偽のブール値はゼロ整数に強制変換されます。したがって、scipyまたはnumpyを使用することに満足している場合は、シーケンスの各要素に整数の配列を作成し、各配列に各テストの1つの要素を含め、配列を合計します。例えば

>>> sum(scipy.array([c % 2 == 0, c % 3 == 0]) for c in xrange(10))
array([5, 4])
于 2008-10-01T16:47:13.433 に答える
1

1 つのリスト内包表記/式を実行して、その統計テストを含む一連のタプルを取得し、それを減らして合計を取得できます。


r=xrange(10)
s=( (v % 2 == 0, v % 3 == 0) for v in r )
def add_tuples(t1,t2):
     return tuple(x+y for x,y in zip(t1, t2))
sums=reduce(add_tuples, s, (0,0)) # (0,0) is starting amount

print sums[0] # sum of numbers divisible by 2
print sums[1] # sum of numbers divisible by 3

ジェネレーター式などを使用すると、イテレーターを 1 回だけ実行することになります (reduce が変なことをしない限り)。基本的に、マップ/リデュースを行うことになります...

于 2008-10-01T10:53:51.893 に答える
0

Alt 3、「ヒット」の数に比例してメモリを使用しないため。xrange(one_trillion) のような病的なケースを考えると、他の提案されたソリューションの多くはひどく失敗します。

于 2008-10-01T16:59:00.037 に答える
0

ここでの考え方は、リダクションを使用して反復の繰り返しを避けることです。また、メモリが問題になる場合、これにより余分なデータ構造が作成されることはありません。カウンター ( ) を持つ辞書から開始し{'div2': 0, 'div3': 0}、反復に沿ってそれらをインクリメントします。

def increment_stats(stats, n):
    if n % 2 == 0: stats['div2'] += 1
    if n % 3 == 0: stats['div3'] += 1
    return stats

r = xrange(1, 10)
stats = reduce(increment_stats, r, {'div2': 0, 'div3': 0})
print stats

除数よりも複雑なものを数えたい場合は、統計抽出のロジックをカプセル化して、よりオブジェクト指向のアプローチを使用することが適切です (同じ利点があります)。

class Stats:

    def __init__(self, div2=0, div3=0):
        self.div2 = div2
        self.div3 = div3

    def increment(self, n):
        if n % 2 == 0: self.div2 += 1
        if n % 3 == 0: self.div3 += 1
        return self

    def __repr__(self):
        return 'Stats(%d, %d)' % (self.div2, self.div3)

r = xrange(1, 10)
stats = reduce(lambda stats, n: stats.increment(n), r, Stats())
print stats

間違いがあればご指摘ください。

@Henrik:最初のアプローチは、ある場所で辞書の初期化を制御し、別の場所で更新する必要があり、文字列を使用して各統計を参照する必要があるため、保守性が低いと思います(属性を持つ代わりに)。そして、この場合、オブジェクト指向がやり過ぎだとは思いません。アプリケーションでは述語とオブジェクトが複雑になると言ったからです。実際、述語が非常に単純な場合は、わざわざ辞書を使用することさえしません。単一の固定サイズのリストで十分です。乾杯 :)

于 2008-10-01T13:32:41.733 に答える
0

数値しかない場合は、反復可能なリストではなく、間違いなくnumpy配列を見ているでしょう。配列に対していくつかの簡潔な算術演算を使用して、ほぼ確実にやりたいことを実行できます。

于 2008-10-01T10:53:12.113 に答える
0

あなたが探しているほど簡潔ではありませんが、より効率的です。実際には、複数回ループできるイテラブルだけでなく、あらゆるイテラブルで機能し、さらに複雑にすることなくチェックするものを拡張できます。

r = xrange(1, 10)

counts = {
   2: 0,
   3: 0,
}

for v in r:
    for q in counts:
        if not v % q:
            counts[q] += 1
        # Or, more obscure:
        #counts[q] += not v % q

for q in counts:
    print "%s's: %s" % (q, counts[q])
于 2008-10-01T10:56:46.770 に答える
0
from itertools import groupby
from collections import defaultdict

def multiples(v):
    return 2 if v%2==0 else 3 if v%3==0 else None
d = defaultdict(list)

for k, values in groupby(range(10), multiples):
    if k is not None:
        d[k].extend(values)
于 2008-10-01T11:26:34.873 に答える
0

上記のOO-stabに触発されて、私もそれを試してみなければなりませんでした(ただし、これは私が解決しようとしている問題にとってはやり過ぎです:)

class Stat(object):
  def update(self, n):
    raise NotImplementedError

  def get(self):
    raise NotImplementedError


class TwoStat(Stat):
  def __init__(self):
    self._twos = 0

  def update(self, n):
    if n % 2 == 0: self._twos += 1

  def get(self):
    return self._twos


class ThreeStat(Stat):
  def __init__(self):
    self._threes = 0

  def update(self, n):
    if n % 3 == 0: self._threes += 1

  def get(self):
    return self._threes


class StatCalculator(object):
  def __init__(self, stats):
    self._stats = stats

  def calculate(self, r):
    for v in r:
      for stat in self._stats:
        stat.update(v)
    return tuple(stat.get() for stat in self._stats)


s = StatCalculator([TwoStat(), ThreeStat()])

r = xrange(1, 10)
print s.calculate(r)
于 2008-10-01T15:35:20.267 に答える