0
# Bags and Multisets
1
2
Bags (also known as multisets) are collections that track element counts, allowing duplicates while maintaining count information. They support mathematical set operations with multiplicity, statistical operations on collections, and efficient counting operations.
3
4
## Capabilities
5
6
### Bag Construction
7
8
Create bags from iterables with automatic element counting.
9
10
```python { .api }
11
class Bag:
12
def __init__(self, iterable=None):
13
"""Create a new bag.
14
15
Args:
16
iterable: Iterable to populate the bag from. Each element will be
17
added however many times it appears.
18
"""
19
20
def bag(iterable=None):
21
"""Create a mutable bag from an iterable."""
22
23
def frozenbag(iterable=None):
24
"""Create an immutable, hashable bag from an iterable."""
25
```
26
27
Usage examples:
28
```python
29
from collections_extended import bag, frozenbag
30
31
# Create from string - counts each character
32
b = bag('abracadabra')
33
print(b) # bag(('a', 'a', 'a', 'a', 'a', 'b', 'b', 'r', 'r', 'c', 'd'))
34
35
# Create from list
36
b = bag([1, 2, 2, 3, 3, 3])
37
print(b.count(3)) # 3
38
39
# Create immutable bag
40
fb = frozenbag('hello')
41
print(fb.count('l')) # 2
42
```
43
44
### Element Counting and Access
45
46
Query element counts and access unique elements efficiently.
47
48
```python { .api }
49
def count(self, value):
50
"""Return the number of times value appears in this bag.
51
52
Args:
53
value: The element to count
54
55
Returns:
56
int: Count of value in the bag (0 if not present)
57
"""
58
59
def num_unique_elements(self):
60
"""Return the number of unique elements.
61
62
Returns:
63
int: Number of distinct elements
64
"""
65
66
def unique_elements(self):
67
"""Return a view of unique elements in this bag.
68
69
Returns:
70
UniqueElementsView: View of unique elements
71
"""
72
73
def counts(self):
74
"""Return a view of (element, count) pairs.
75
76
Returns:
77
CountsView: View of element-count pairs
78
"""
79
```
80
81
### Mutable Bag Operations
82
83
Modify bag contents by adding, removing, and updating elements.
84
85
```python { .api }
86
def add(self, elem):
87
"""Add elem to the bag (increment its count by 1)."""
88
89
def remove(self, elem):
90
"""Remove one occurrence of elem from the bag.
91
92
Raises:
93
ValueError: If elem is not in the bag
94
"""
95
96
def discard(self, elem):
97
"""Remove one occurrence of elem from the bag if present."""
98
99
def pop(self):
100
"""Remove and return an arbitrary element from the bag.
101
102
Returns:
103
Any: An element from the bag
104
105
Raises:
106
KeyError: If bag is empty
107
"""
108
109
def clear(self):
110
"""Remove all elements from the bag."""
111
112
def discard_all(self, other):
113
"""Remove all elements from other, ignoring missing elements."""
114
115
def remove_all(self, other):
116
"""Remove all elements from other.
117
118
Raises:
119
ValueError: If any element in other has higher count than in self
120
"""
121
```
122
123
### Set Operations with Multiplicity
124
125
Mathematical set operations that preserve element counts.
126
127
```python { .api }
128
def __add__(self, other):
129
"""Return new bag with elements from both bags (counts added)."""
130
131
def __sub__(self, other):
132
"""Return new bag with elements of other removed from self."""
133
134
def __and__(self, other):
135
"""Return intersection (minimum counts for each element)."""
136
137
def __or__(self, other):
138
"""Return union (maximum counts for each element)."""
139
140
def __xor__(self, other):
141
"""Return symmetric difference (absolute difference of counts)."""
142
143
def issubset(self, other):
144
"""Check if every element in self has count <= in other."""
145
146
def issuperset(self, other):
147
"""Check if every element in self has count >= in other."""
148
149
def isdisjoint(self, other):
150
"""Return True if bags have no elements in common."""
151
```
152
153
Usage examples:
154
```python
155
a = bag('aab') # a:2, b:1
156
b = bag('abc') # a:1, b:1, c:1
157
158
print(a + b) # bag: a:3, b:2, c:1
159
print(a - b) # bag: a:1 (2-1)
160
print(a & b) # bag: a:1, b:1 (minimums)
161
print(a | b) # bag: a:2, b:1, c:1 (maximums)
162
print(a.issubset(bag('aaabbc'))) # True
163
```
164
165
### Statistical and Advanced Operations
166
167
Advanced operations for analysis and manipulation.
168
169
```python { .api }
170
def product(self, other, operator=None):
171
"""Cartesian product of two bags.
172
173
Args:
174
other: Another iterable
175
operator: Optional function to combine elements instead of creating tuples
176
177
Returns:
178
bag: New bag with product elements
179
"""
180
181
@classmethod
182
def from_mapping(cls, mapping):
183
"""Create a bag from a dict of element->count.
184
185
Args:
186
mapping: Dict mapping elements to their counts
187
188
Raises:
189
ValueError: If any count is negative
190
"""
191
192
def copy(self):
193
"""Return a shallow copy of the bag."""
194
```
195
196
### Immutable Bags
197
198
Create hashable, immutable versions of bags for use as dictionary keys or set elements.
199
200
```python { .api }
201
class frozenbag(Bag, Hashable):
202
def __hash__(self):
203
"""Return hash value for the frozenbag."""
204
```
205
206
Usage examples:
207
```python
208
from collections_extended import frozenbag
209
210
# Immutable bags can be used as dict keys
211
fb1 = frozenbag('abc')
212
fb2 = frozenbag('def')
213
mapping = {fb1: 'first', fb2: 'second'}
214
215
# Or in sets
216
bag_set = {frozenbag('hello'), frozenbag('world')}
217
```
218
219
### View Classes
220
221
Specialized views providing different perspectives on bag contents.
222
223
```python { .api }
224
class UniqueElementsView:
225
"""A view of unique elements in a bag."""
226
def __iter__(self): ...
227
def __contains__(self, elem): ...
228
def __len__(self): ...
229
230
class CountsView:
231
"""A view of (element, count) pairs in a bag."""
232
def __iter__(self): ...
233
def __contains__(self, item): ...
234
def __len__(self): ...
235
```
236
237
Usage examples:
238
```python
239
b = bag('hello world')
240
241
# Iterate over unique elements
242
for elem in b.unique_elements():
243
print(f"{elem}: {b.count(elem)}")
244
245
# Iterate over (element, count) pairs
246
for elem, count in b.counts():
247
print(f"{elem} appears {count} times")
248
249
# Check if specific count exists
250
print(('l', 3) in b.counts()) # True
251
```