Simplify explanation of multiset operations by removing restrictions on negative inputs.
This commit is contained in:
parent
63b3a97a2a
commit
e0d1b9f11f
|
@ -253,16 +253,18 @@ Common patterns for working with :class:`Counter` objects::
|
||||||
c.items() # convert to a list of (elem, cnt) pairs
|
c.items() # convert to a list of (elem, cnt) pairs
|
||||||
Counter(dict(list_of_pairs)) # convert from a list of (elem, cnt) pairs
|
Counter(dict(list_of_pairs)) # convert from a list of (elem, cnt) pairs
|
||||||
c.most_common()[:-n:-1] # n least common elements
|
c.most_common()[:-n:-1] # n least common elements
|
||||||
|
c += Counter() # remove zero and negative counts
|
||||||
|
|
||||||
Several multiset mathematical operations are provided for combining
|
Several multiset mathematical operations are provided for combining
|
||||||
:class:`Counter` objects. Multisets are like regular sets but allowed to
|
:class:`Counter` objects. Multisets are like regular sets but are allowed to
|
||||||
contain repeated elements (with counts of one or more). Addition and
|
contain repeated elements (with counts of one or more). Addition and
|
||||||
subtraction combine counters by adding or subtracting the counts of
|
subtraction combine counters by adding or subtracting the counts of
|
||||||
corresponding elements. Intersection and union return the minimum and maximum
|
corresponding elements. Intersection and union return the minimum and maximum
|
||||||
of corresponding counts::
|
of corresponding counts. All four multiset operations exclude results with
|
||||||
|
zero or negative counts::
|
||||||
|
|
||||||
>>> c = Counter({'a': 3, 'b': 1})
|
>>> c = Counter(a=3, b=1)
|
||||||
>>> d = Counter({'a': 1, 'b': 2})
|
>>> d = Counter(a=1, b=2)
|
||||||
>>> c + d # add two counters together: c[x] + d[x]
|
>>> c + d # add two counters together: c[x] + d[x]
|
||||||
Counter({'a': 4, 'b': 3})
|
Counter({'a': 4, 'b': 3})
|
||||||
>>> c - d # subtract (keeping only positive counts)
|
>>> c - d # subtract (keeping only positive counts)
|
||||||
|
@ -272,16 +274,6 @@ of corresponding counts::
|
||||||
>>> c | d # union: max(c[x], d[x])
|
>>> c | d # union: max(c[x], d[x])
|
||||||
Counter({'a': 3, 'b': 2})
|
Counter({'a': 3, 'b': 2})
|
||||||
|
|
||||||
All four multiset operations produce only positive counts (negative and zero
|
|
||||||
results are skipped). If inputs include negative counts, addition will sum
|
|
||||||
both counts and then exclude non-positive results. The other three operations
|
|
||||||
are undefined for negative inputs::
|
|
||||||
|
|
||||||
>>> e = Counter(a=8, b=-2, c=0)
|
|
||||||
>>> e += Counter() # remove zero and negative counts
|
|
||||||
>>> e
|
|
||||||
Counter({'a': 8})
|
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
* `Bag class <http://www.gnu.org/software/smalltalk/manual-base/html_node/Bag.html>`_
|
* `Bag class <http://www.gnu.org/software/smalltalk/manual-base/html_node/Bag.html>`_
|
||||||
|
|
|
@ -314,8 +314,8 @@ class Counter(dict):
|
||||||
if not isinstance(other, Counter):
|
if not isinstance(other, Counter):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
result = Counter()
|
result = Counter()
|
||||||
for elem, count in self.items():
|
for elem in set(self) | set(other):
|
||||||
newcount = count - other[elem]
|
newcount = self[elem] - other[elem]
|
||||||
if newcount > 0:
|
if newcount > 0:
|
||||||
result[elem] = newcount
|
result[elem] = newcount
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -462,18 +462,19 @@ class TestCounter(unittest.TestCase):
|
||||||
for i in range(1000):
|
for i in range(1000):
|
||||||
# test random pairs of multisets
|
# test random pairs of multisets
|
||||||
p = Counter(dict((elem, randrange(-2,4)) for elem in elements))
|
p = Counter(dict((elem, randrange(-2,4)) for elem in elements))
|
||||||
|
p.update(e=1, f=-1, g=0)
|
||||||
q = Counter(dict((elem, randrange(-2,4)) for elem in elements))
|
q = Counter(dict((elem, randrange(-2,4)) for elem in elements))
|
||||||
for counterop, numberop, defneg in [
|
q.update(h=1, i=-1, j=0)
|
||||||
(Counter.__add__, lambda x, y: x+y if x+y>0 else 0, True),
|
for counterop, numberop in [
|
||||||
(Counter.__sub__, lambda x, y: x-y if x-y>0 else 0, False),
|
(Counter.__add__, lambda x, y: max(0, x+y)),
|
||||||
(Counter.__or__, max, False),
|
(Counter.__sub__, lambda x, y: max(0, x-y)),
|
||||||
(Counter.__and__, min, False),
|
(Counter.__or__, lambda x, y: max(0,x,y)),
|
||||||
|
(Counter.__and__, lambda x, y: max(0, min(x,y))),
|
||||||
]:
|
]:
|
||||||
result = counterop(p, q)
|
result = counterop(p, q)
|
||||||
for x in elements:
|
for x in elements:
|
||||||
# all except __add__ are undefined for negative inputs
|
self.assertEqual(numberop(p[x], q[x]), result[x],
|
||||||
if defneg or (p[x] >= 0 and q[x] >= 0):
|
(counterop, x, p, q))
|
||||||
self.assertEqual(numberop(p[x], q[x]), result[x])
|
|
||||||
# verify that results exclude non-positive counts
|
# verify that results exclude non-positive counts
|
||||||
self.assert_(x>0 for x in result.values())
|
self.assert_(x>0 for x in result.values())
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue