Skip to content

Generators

toolbox_python.generators 🔗

Summary

This module provides functions to generate information as needed. Such functions are typically used to generate data that is not stored in a database or file, but rather computed on-the-fly based on input parameters.

generate_group_cutoffs 🔗

generate_group_cutoffs(
    total_number: int, num_groups: int
) -> tuple[tuple[int, int], ...]

Summary

Generate group cutoffs for a given total number and number of groups.

Details

This function divides a total number of items into a specified number of groups, returning a tuple of tuple's where each inner tuple contains the start and end indices for each group. The last group may contain fewer items if the total number is not evenly divisible by the number of groups.

Parameters:

Name Type Description Default
total_number int

The total number of items to be divided into groups.

required
num_groups int

The number of groups to create.

required

Raises:

Type Description
TypeCheckError

If any of the inputs parsed to the parameters of this function are not the correct type. Uses the @typeguard.typechecked decorator.

ValueError

If total_number is less than 1, or if num_groups is less than 1, or if total_number is less than num_groups. Uses the assert_is_valid function to validate the inputs.

Returns:

Type Description
tuple[tuple[int, int], ...]

A tuple of tuples, where each inner tuple contains the start and end indices for each group. The last group may have a different size if the total number is not evenly divisible by the number of groups.

Examples
Prepare data
1
>>> from toolbox_python.generators import generate_group_cutoffs

Example 1: Basic usage
1
>>> generate_group_cutoffs(10, 3)
Output
((0, 3), (3, 6), (6, 11))

Conclusion: Successfully split 10 items into 3 groups.

Example 2: Uneven groups
1
>>> generate_group_cutoffs(10, 4)
Output
((0, 3), (3, 6), (6, 9), (9, 11))

Conclusion: Successfully split 10 items into 4 groups, with the last group having fewer items.

Example 3: Single group
1
>>> generate_group_cutoffs(10, 1)
Output
((0, 11),)

Conclusion: Successfully created a single group containing all items.

Example 4: Zero groups
1
>>> generate_group_cutoffs(10, 0)
Output
ValueError: Validation failed: 'num_groups > 0' is not True

Conclusion: Cannot create groups with zero groups specified.

Example 5: Negative total number
1
>>> generate_group_cutoffs(-10, 3)
Output
ValueError: Validation failed: 'total_number > 0' is not True

Conclusion: Total number must be greater than 0.

Example 6: Total number less than groups
1
>>> generate_group_cutoffs(3, 5)
Output
ValueError: Validation failed: 'total_number >= num_groups' is not True

Conclusion: Total number must be greater than or equal to the number of groups.

Source code in src/toolbox_python/generators.py
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
@typechecked
def generate_group_cutoffs(
    total_number: int, num_groups: int
) -> tuple[tuple[int, int], ...]:
    """
    !!! note "Summary"
        Generate group cutoffs for a given total number and number of groups.

    !!! details "Details"
        This function divides a total number of items into a specified number of groups, returning a `#!py tuple` of `#!py tuple`'s where each inner `#!py tuple` contains the start and end indices for each group. The last group may contain fewer items if the total number is not evenly divisible by the number of groups.

    Params:
        total_number (int):
            The total number of items to be divided into groups.
        num_groups (int):
            The number of groups to create.

    Raises:
        TypeCheckError:
            If any of the inputs parsed to the parameters of this function are not the correct type. Uses the [`@typeguard.typechecked`](https://typeguard.readthedocs.io/en/stable/api.html#typeguard.typechecked) decorator.
        ValueError:
            If `total_number` is less than 1, or if `num_groups` is less than 1, or if `total_number` is less than `num_groups`. Uses the [`assert_is_valid`][toolbox_python.checkers.assert_is_valid] function to validate the inputs.

    Returns:
        (tuple[tuple[int, int], ...]):
            A tuple of tuples, where each inner tuple contains the start and end indices for each group. The last group may have a different size if the total number is not evenly divisible by the number of groups.

    ???+ example "Examples"

        ```pycon {.py .python linenums="1" title="Prepare data"}
        >>> from toolbox_python.generators import generate_group_cutoffs
        ```

        ```pycon {.py .python linenums="1" title="Example 1: Basic usage"}
        >>> generate_group_cutoffs(10, 3)
        ```
        <div class="result" markdown>
        ```{.sh .shell title="Output"}
        ((0, 3), (3, 6), (6, 11))
        ```
        !!! success "Conclusion: Successfully split 10 items into 3 groups."
        </div>

        ```pycon {.py .python linenums="1" title="Example 2: Uneven groups"}
        >>> generate_group_cutoffs(10, 4)
        ```
        <div class="result" markdown>
        ```{.sh .shell title="Output"}
        ((0, 3), (3, 6), (6, 9), (9, 11))
        ```
        !!! success "Conclusion: Successfully split 10 items into 4 groups, with the last group having fewer items."
        </div>

        ```pycon {.py .python linenums="1" title="Example 3: Single group"}
        >>> generate_group_cutoffs(10, 1)
        ```
        <div class="result" markdown>
        ```{.sh .shell title="Output"}
        ((0, 11),)
        ```
        !!! success "Conclusion: Successfully created a single group containing all items."
        </div>

        ```pycon {.py .python linenums="1" title="Example 4: Zero groups"}
        >>> generate_group_cutoffs(10, 0)
        ```
        <div class="result" markdown>
        ```{.sh .shell title="Output"}
        ValueError: Validation failed: 'num_groups > 0' is not True
        ```
        !!! failure "Conclusion: Cannot create groups with zero groups specified."
        </div>

        ```pycon {.py .python linenums="1" title="Example 5: Negative total number"}
        >>> generate_group_cutoffs(-10, 3)
        ```
        <div class="result" markdown>
        ```{.sh .shell title="Output"}
        ValueError: Validation failed: 'total_number > 0' is not True
        ```
        !!! failure "Conclusion: Total number must be greater than 0."
        </div>

        ```pycon {.py .python linenums="1" title="Example 6: Total number less than groups"}
        >>> generate_group_cutoffs(3, 5)
        ```
        <div class="result" markdown>
        ```{.sh .shell title="Output"}
        ValueError: Validation failed: 'total_number >= num_groups' is not True
        ```
        !!! failure "Conclusion: Total number must be greater than or equal to the number of groups."
        </div>
    """

    # Validations
    assert_is_valid(total_number, ">", 0)
    assert_is_valid(num_groups, ">", 0)
    assert_is_valid(total_number, ">=", num_groups)

    # Calculate the size of each group
    group_size: int = total_number // num_groups

    # List to store all group cutoffs
    cutoffs: list[tuple[int, int]] = []

    # Calculate the number of items that will be in the last group
    current_start: int = 0

    # Loop through the number of groups to calculate start and end indices
    for group in range(num_groups):

        # For the last group, end is total_number + 1
        if group == num_groups - 1:
            current_end: int = total_number + 1
        else:
            current_end: int = current_start + group_size

        # Add the current group cutoff to the list
        cutoffs.append((current_start, current_end))

        # Update the start index for the next group
        current_start: int = current_end

    # Convert the list to a tuple of tuples
    return tuple(cutoffs)