<jupyter_start><jupyter_text>SimulationLet's add uncertainty around the spf exponent.<jupyter_code>rg = default_rng(4)
num_sim_reps = 1000
random_inputs = {'spf_exponent': rg.uniform(-2.0, -1.6, size=num_sim_reps)}
sim_results = model.simulate(random_inputs, ['profit'])
sim_results_df = get_sim_results_df(sim_results)

plt.title("Profit distribution")
plt.xlabel("Profit")
plt.ylabel("density")
plt.hist(sim_results_df['profit'], density=True);
plt.show();<jupyter_output><empty_output><jupyter_text>Extended model<jupyter_code>class SingleProductSPF_extended(Model):
    def __init__(self, fixed_cost=60e+3, var_cost=90, selling_price=120,
                 spf_scale=10e+6, spf_exponent=-1.8,
                 ot_threshold=1000, ot_var_cost=110, lost_demand_threshold=1200):
        
        self.fixed_cost = fixed_cost
        self.var_cost = var_cost
        self.selling_price = selling_price
        self.spf_scale = spf_scale
        self.spf_exponent = spf_exponent
        self.ot_threshold = ot_threshold
        self.ot_var_cost = ot_var_cost
        self.lost_demand_threshold = lost_demand_threshold

    def demand(self):
        """Demand based on SPF power function
        
        D = a * P ** b
        """
        
        _demand = self.spf_scale * self.selling_price ** self.spf_exponent
        
        return _demand
    
    def units_sold(self):
        """Number of units sold
        
        Min of D and lost_demand_threshold
        """
        
        demand = self.demand()
        _units_sold = np.minimum(demand, self.lost_demand_threshold)
        return _units_sold
    
    def tot_revenue(self):
        """Total revenue
        
        D * P
        """
        
        _tot_revenue = self.units_sold() * self.selling_price
        
        return _tot_revenue
    
    def tot_var_cost(self):
        
        units_sold = self.units_sold()
        units_at_base_cost = np.minimum(units_sold, self.ot_threshold)
        units_at_ot_cost = np.maximum(0, units_sold - self.ot_threshold)
        
        _tot_var_cost = self.var_cost * units_at_base_cost + self.ot_var_cost * units_at_ot_cost
        return _tot_var_cost
    
    def tot_cost(self):
        """Total cost
        
        Fixed Cost + Var Cost * P
        """
        
        _tot_cost = self.fixed_cost + self.tot_var_cost()
        
        return _tot_cost

    def profit(self):
        """Profit
        
        Total revenue - total cost
        """
        
        _profit = self.tot_revenue() - self.tot_cost()
        
        return _profit
model_x = SingleProductSPF_extended()
print(model_x.units_sold())
print(model_x.tot_var_cost())
print(model_x.profit())
dt_selling_price_x = model_x.data_table(selling_prices, ['profit', 'demand', 'units_sold', ])
dt_selling_price_x
plt.title("Sensitivity of profit to selling price")
plt.xlabel("Selling price")
plt.ylabel("Profit")
plt.plot(dt_selling_price_x['selling_price'], dt_selling_price_x['profit'])
plt.hlines(0, dt_selling_price_x['selling_price'].min(), dt_selling_price_x['selling_price'].max(),
          linestyles='--')
plt.show();
print(f"Break even selling price: {model_x.goal_seek('profit', 0, 'selling_price', 90, 250):.2f}")
print(f"Break even selling price: {model_x.goal_seek('profit', 0, 'selling_price', 200, 500):.2f}")<jupyter_output>Break even selling price: 454.47
{'fraction_non_alphanumeric': 0.10975609756097561, 'fraction_numerical': 0.01635930993456276, 'mean_word_length': 2.8878612716763006, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '43132', 'license': 'MIT', 'n_tokens_mistral': 1192, 'n_tokens_neox': 1112, 'n_words': 195, 'path': 'examples/single_product_spf.ipynb', 'repo_name': 'misken/whatif', 'size': '3362'}