• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

j2kun / pmfp-code / 15550770199

10 Jun 2025 04:44AM UTC coverage: 99.046% (-0.8%) from 99.837%
15550770199

Pull #85

github

web-flow
Merge 0b8c72c6b into 7f9f6bc31
Pull Request #85: Tip for single sampling scheme

36 of 51 new or added lines in 1 file covered. (70.59%)

1868 of 1886 relevant lines covered (99.05%)

0.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.59
/tips/single_sampling_plan.py
1
import random
1✔
2
from dataclasses import dataclass
1✔
3

4
import scipy.stats
1✔
5

6

7
@dataclass
1✔
8
class SingleSamplingPlan:
1✔
9
    # The number of items to sample from the lot
10
    sample_size: int
1✔
11

12
    # The maximum number of defective items in the sample before the lot is
13
    # rejected
14
    acceptance_number: int
1✔
15

16

17
def hypergeom(
1✔
18
    sample_size: int,
19
    acceptance_number: int,
20
    population_size: int,
21
    defective_count: int,
22
) -> float:
23
    """Calculate the hypergeometric function as used by the single sampling plan."""
24
    return scipy.stats.hypergeom.cdf(
1✔
25
        k=acceptance_number,  # accept if we find <= c defects in the sample
26
        M=population_size,  # population size
27
        n=sample_size,  # sample size
28
        N=defective_count,  # number of defects in the population
29
    )
30

31

32
def find_plan_hypergeom(
1✔
33
    producer_risk_point: tuple[float, float],
34
    consumer_risk_point: tuple[float, float],
35
    population_size: int,
36
) -> SingleSamplingPlan:
37
    """Find the best single sampling plan.
38

39
    Args:
40
        producer_risk_point: (d, p) implying lots with defect rate <= d are
41
            accepted with probability >= p
42
        consumer_risk_point: (d, p) implying lots with defect rate >= d are
43
            accepted with probability <= p
44
        population_size: Size of the lot
45

46
    Returns: a tuple containing the best sample size and number of defects allowed.
47
    """
48
    assert 0 < producer_risk_point[0] < 1
1✔
49
    assert 0 < producer_risk_point[1] < 1
1✔
50
    assert 0 < consumer_risk_point[0] < 1
1✔
51
    assert 0 < consumer_risk_point[1] < 1
1✔
52

53
    defects_accepted = 0
1✔
54
    sample_size = defects_accepted + 1
1✔
55

56
    while True:
1✔
57
        prob_accept_bad_lot = hypergeom(
1✔
58
            sample_size=sample_size,
59
            acceptance_number=defects_accepted,
60
            population_size=population_size,
61
            defective_count=int(consumer_risk_point[0] * population_size),
62
        )
63
        prob_accept_good_lot = hypergeom(
1✔
64
            sample_size=sample_size,
65
            acceptance_number=defects_accepted,
66
            population_size=population_size,
67
            defective_count=int(producer_risk_point[0] * population_size),
68
        )
69

70
        if prob_accept_bad_lot > consumer_risk_point[1]:
1✔
71
            sample_size += 1  # Increase sample size to be more stringent
1✔
72
        elif prob_accept_good_lot < producer_risk_point[1]:
1✔
73
            defects_accepted += 1  # Allow one more defect to be more lenient
1✔
74
        else:
75
            # If both checks pass, the plan is good
76
            break
1✔
77

78
    return SingleSamplingPlan(sample_size, defects_accepted)
1✔
79

80

81
def simulate_single_sampling_plan(
1✔
82
    population_size: int,
83
    actual_defective: int,
84
    plan: tuple[int, int],
85
    seed: int = 0,
86
    rounds: int = 1000,
87
):
88
    items = list(range(population_size))
1✔
89
    random.seed(seed)
1✔
90

91
    accepted_count = 0
1✔
92
    for i in range(rounds):
1✔
93
        defective_items = random.sample(items, actual_defective)
1✔
94
        sample = random.sample(items, plan[0])
1✔
95
        accepted = len(set(sample) & set(defective_items)) <= plan[1]
1✔
96
        accepted_count += int(accepted)
1✔
97

98
    return accepted_count / rounds
1✔
99

100

101
def plot_plan(
1✔
102
    population_size: int,
103
    plan: SingleSamplingPlan,
104
    filename: str,
105
):
106
    """Plot the Operating Characteristic (OC) probability for a single-stage
107
    hypergeometric sampling plan.
108

109
    The graph depicts the probability of accepting the lot given the sampling plan.
110
    """
NEW
111
    import matplotlib
×
NEW
112
    import matplotlib.pyplot as plt
×
113

NEW
114
    font = {"size": 14}
×
NEW
115
    matplotlib.rc("font", **font)
×
116

NEW
117
    defective_count = list(range(500))
×
NEW
118
    p_accept = [
×
119
        hypergeom(plan.sample_size, plan.acceptance_number, population_size, defects)
120
        for defects in defective_count
121
    ]
NEW
122
    print(
×
123
        f"Sample size: {plan.sample_size}, Acceptance number: {plan.acceptance_number}",
124
    )
125

NEW
126
    plt.plot(
×
127
        defective_count,
128
        p_accept,
129
        linewidth=4,
130
    )
NEW
131
    plt.xlabel("Actual defect count", fontsize=16)
×
NEW
132
    plt.ylabel("Acceptance probability", fontsize=16)
×
133

NEW
134
    plt.scatter(
×
135
        [producer_risk_point[0] * population_size],
136
        [producer_risk_point[1]],
137
        color="red",
138
        s=100,
139
        zorder=5,
140
    )
NEW
141
    plt.scatter(
×
142
        [consumer_risk_point[0] * population_size],
143
        [consumer_risk_point[1]],
144
        color="black",
145
        s=100,
146
        zorder=5,
147
    )
148

NEW
149
    plt.tight_layout()
×
NEW
150
    plt.savefig(filename, dpi=300)
×
NEW
151
    plt.clf()
×
152

153

154
if __name__ == "__main__":
155

156
    population_size = 1000
157

158
    # plot two points on the graph
159
    producer_risk_point = (0.05, 0.95)  # (defect rate, acceptance probability)
160
    consumer_risk_point = (0.15, 0.05)  # (defect rate, acceptance probability)
161
    # find the plan that satisfies these points
162
    good_plan = find_plan_hypergeom(
163
        producer_risk_point,
164
        consumer_risk_point,
165
        population_size,
166
    )
167

168
    plot_plan(population_size, good_plan, "good_oc_curve.pdf")
169

170
    # This represents a bad plan for the consumer risk point below. The plan
171
    # will accept a lot with too many defects.
172
    bad_plan = SingleSamplingPlan(
173
        sample_size=50,
174
        acceptance_number=10,
175
    )
176
    plot_plan(population_size, bad_plan, "bad_oc_curve.pdf")
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc