Skip to content

Commit 09d5c28

Browse files
committed
-
1 parent 3a15671 commit 09d5c28

File tree

1 file changed

+87
-72
lines changed
  • source_py3/python_toolbox/sequence_tools

1 file changed

+87
-72
lines changed

source_py3/python_toolbox/sequence_tools/misc.py

Lines changed: 87 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -53,78 +53,93 @@ class NO_FILL_VALUE(misc_tools.NonInstantiable):
5353
'''
5454

5555

56-
def partitions(sequence, partition_size=None, n_partitions=None,
57-
allow_remainder=True, fill_value=NO_FILL_VALUE):
58-
'''
59-
Partition `sequence` into equal partitions of size `partition_size`, or
60-
determine size automatically given the number of partitions as
61-
`n_partitions`.
62-
63-
If the sequence can't be divided into precisely equal partitions, the last
64-
partition will contain less members than all the other partitions.
65-
66-
Example:
67-
68-
>>> partitions([0, 1, 2, 3, 4], 2)
69-
[[0, 1], [2, 3], [4]]
70-
71-
(You need to give *either* a `partition_size` *or* an `n_partitions`
72-
argument, not both.)
73-
74-
Specify `allow_remainder=False` to enforce that the all the partition sizes
75-
be equal; if there's a remainder while `allow_remainder=False`, an
76-
exception will be raised.
77-
78-
If you want the remainder partition to be of equal size with the other
79-
partitions, you can specify `fill_value` as the padding for the last
80-
partition. A specified value for `fill_value` implies
81-
`allow_remainder=True` and will cause an exception to be raised if
82-
specified with `allow_remainder=False`.
83-
84-
Example:
85-
86-
>>> partitions([0, 1, 2, 3, 4], 3, fill_value=None)
87-
[[0, 1, 2], [3, 4, None]]
88-
89-
'''
90-
91-
sequence_length = len(sequence)
92-
93-
### Validating input: #####################################################
94-
# #
95-
if (partition_size is None) == (n_partitions is None):
96-
raise Exception('You must specify *either* `partition_size` *or* '
97-
'`n_paritions`.')
98-
99-
if fill_value is not NO_FILL_VALUE and not allow_remainder:
100-
raise ValueError('`fill_value` cannot be specified if '
101-
'`allow_remainder` is `False`.')
102-
103-
remainder_length = sequence_length % (partition_size if partition_size
104-
is not None else n_partitions)
105-
106-
if not allow_remainder and remainder_length > 0:
107-
raise Exception("You set `allow_remainder=False`, but there's a "
108-
"reminder of %s left." % remainder_length)
109-
# #
110-
### Finished validating input. ############################################
111-
112-
if partition_size is None:
113-
partition_size = math_tools.ceil_div(sequence_length, n_partitions)
114-
if n_partitions is None:
115-
n_partitions = math_tools.ceil_div(sequence_length, partition_size)
116-
117-
enlarged_length = partition_size * n_partitions
118-
119-
blocks = [sequence[i : i + partition_size] for i in
120-
range(0, enlarged_length, partition_size)]
121-
122-
if fill_value is not NO_FILL_VALUE and blocks:
123-
filler = itertools.repeat(fill_value,
124-
enlarged_length - sequence_length)
125-
blocks[-1].extend(filler)
126-
127-
return blocks
56+
def partitions(sequence, partition_size=None, n_partitions=None,
57+
allow_remainder=True, larger_on_remainder=False,
58+
fill_value=NO_FILL_VALUE):
59+
'''
60+
Partition `sequence` into equal partitions of size `partition_size`, or
61+
determine size automatically given the number of partitions as
62+
`n_partitions`.
63+
64+
If the sequence can't be divided into precisely equal partitions, the last
65+
partition will contain less members than all the other partitions.
66+
67+
Example:
68+
69+
>>> partitions([0, 1, 2, 3, 4], 2)
70+
[[0, 1], [2, 3], [4]]
71+
72+
(You need to give *either* a `partition_size` *or* an `n_partitions`
73+
argument, not both.)
74+
75+
Specify `allow_remainder=False` to enforce that the all the partition sizes
76+
be equal; if there's a remainder while `allow_remainder=False`, an
77+
exception will be raised.
78+
79+
By default, if there's a remainder, the last partition will be smaller than
80+
the others. (e.g. a sequence of 7 items, when partitioned into pairs, will
81+
have 3 pairs and then a partition with only 1 element.) Specify
82+
`larger_on_remainder=True` to make the last partition, in case there is
83+
one, be a bigger partition. (e.g. a sequence of a 7 items divided into
84+
pairs would result in 2 pairs and one triplet.)
85+
86+
If you want the remainder partition to be of equal size with the other
87+
partitions, you can specify `fill_value` as the padding for the last
88+
partition. A specified value for `fill_value` implies
89+
`allow_remainder=True` and will cause an exception to be raised if
90+
specified with `allow_remainder=False`.
91+
92+
Example:
93+
94+
>>> partitions([0, 1, 2, 3, 4], 3, fill_value='meow')
95+
[[0, 1, 2], [3, 4, 'meow']]
96+
97+
'''
98+
99+
sequence_length = len(sequence)
100+
101+
### Validating input: #####################################################
102+
# #
103+
if (partition_size is None) + (n_partitions is None) != 1:
104+
raise Exception('You must specify *either* `partition_size` *or* '
105+
'`n_paritions`.')
106+
107+
remainder_length = sequence_length % (partition_size if partition_size
108+
is not None else n_partitions)
109+
110+
if not allow_remainder and remainder_length > 0:
111+
raise Exception("You set `allow_remainder=False`, but there's a "
112+
"reminder of %s left." % remainder_length)
113+
# #
114+
### Finished validating input. ############################################
115+
116+
if partition_size is None:
117+
if larger_on_remainder:
118+
partition_size = sequence_length // n_partitions # (Floor div)
119+
else:
120+
partition_size = math_tools.ceil_div(sequence_length, n_partitions)
121+
if n_partitions is None:
122+
n_partitions = math_tools.ceil_div(sequence_length, partition_size)
123+
124+
enlarged_length = partition_size * n_partitions
125+
126+
blocks = [sequence[i : i + partition_size] for i in
127+
range(0, enlarged_length, partition_size)]
128+
129+
if enlarged_length > sequence_length: # We have a remainder
130+
assert blocks
131+
if larger_on_remainder:
132+
if len(blocks) >= 2:
133+
small_block_to_append_back = blocks[-1]
134+
del blocks[-1]
135+
blocks[-1] += small_block_to_append_back
136+
elif fill_value != NO_FILL_VALUE: # (We use elif because fill is never
137+
# done if `larger_on_remainder=True`.)
138+
filler = itertools.repeat(fill_value,
139+
enlarged_length - sequence_length)
140+
blocks[-1].extend(filler)
141+
142+
return blocks
128143

129144

130145
def is_immutable_sequence(thing):

0 commit comments

Comments
 (0)