Skip to main content

What is Pricing Simulation?

Pricing simulation helps you understand how different prices will impact your sales and revenue. By testing multiple price points, you can:
  • Predict demand at different price levels
  • Find optimal pricing that maximizes revenue

1. Load Historical Sales Data

This dataset comes from a Fortune 500 company that sells a popular health product in a pharmacy. We’ll use their real sales and pricing history to uncover how data can drive smarter, more profitable pricing decisions.
import asyncio

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from synthefy.api_client import SynthefyAsyncAPIClient

# Load the historical sales data
history_df = pd.read_csv(
    "https://drive.google.com/uc?export=download&id=1FtLW17XE1NHcV1bF8mLW_2WHVKzrHts_"
)
future_df = pd.read_csv(
    "https://drive.google.com/uc?export=download&id=1l2zG7GNTcdDm_HzKhhwii3GTI0Z99a9h"
)
Data Format: Your CSV should have columns like date, unit_price, and sales. The future_df represents the time periods you want to forecast for (without the sales column filled in yet).

Step 2: Visualize Historical Data

Before running simulations, it’s important to understand your historical data. Let’s create visualizations to see how price and sales have changed over time, and whether there’s a correlation.

def plot_historical_analysis(history_df):
    """
    Create two plots to analyze historical price and sales data.

    Plot 1: Time series showing both unit price and sales over time (dual-axis)
    Plot 2: Correlation plot between unit price and sales

    Args:
        history_df: DataFrame with 'date', 'unit_price', and 'sales' columns
    """
    # Create figure with 2 rows and 1 column (vertical layout)
    fig = plt.figure(figsize=(14, 10))

    # --- Plot 1: Historical Time Series Plot ---
    ax1 = plt.subplot(2, 1, 1)

    # Plot unit price as a line
    sns.lineplot(
        data=history_df,
        x="date",
        y="unit_price",
        marker="o",
        label="Unit Price",
        color="C0",
        ax=ax1,
    )
    ax1.set_ylabel("Unit Price", color="C0", fontsize=12)
    ax1.tick_params(axis="y", labelcolor="C0")
    ax1.set_xlabel("Date", fontsize=12)

    # Create a twin axis for sales
    ax2 = ax1.twinx()
    sns.lineplot(
        data=history_df,
        x="date",
        y="sales",
        marker="s",
        label="Sales",
        color="C1",
        ax=ax2,
    )
    ax2.set_ylabel("Sales", color="C1", fontsize=12)
    ax2.tick_params(axis="y", labelcolor="C1")

    # Only show a subset of date xtick labels for readability
    n_dates = len(history_df["date"])
    step = max(n_dates // 10, 1)
    xticks_locs = history_df["date"].iloc[::step]
    ax1.set_xticks(xticks_locs)
    ax1.set_xticklabels(xticks_locs, rotation=45, ha="right")

    # Combine legends from both axes
    lns1, labs1 = ax1.get_legend_handles_labels()
    lns2, labs2 = ax2.get_legend_handles_labels()
    ax2.legend(lns1 + lns2, labs1 + labs2, loc="upper left", frameon=True)

    ax1.set_title(
        "Historical Unit Price and Sales Over Time",
        fontsize=13,
        weight="semibold",
    )

    # --- Plot 2: Correlation Plot ---
    ax3 = plt.subplot(2, 1, 2)

    corr_val = (
        history_df[["unit_price", "sales"]]
        .corr()
        .loc["unit_price", "sales"]
    )

    sns.regplot(
        x="unit_price",
        y="sales",
        data=history_df,
        marker="o",
        color="C2",
        line_kws={"color": "C3", "lw": 2, "label": f"Corr={corr_val:.2f}"},
        ax=ax3,
        scatter_kws={"alpha": 1.0},   # No scatter shading, points fully opaque
        ci=None,                      # Remove confidence interval shading (the red band)
    )
    ax3.set_xlabel("Unit Price ($)", fontsize=12)
    ax3.set_ylabel("Sales", fontsize=12)
    ax3.set_title(
        "Correlation Between Unit Price and Sales",
        fontsize=13,
        weight="semibold",
    )
    ax3.legend()
    ax3.grid(True, linestyle="--", alpha=0.4)

    plt.tight_layout()
    plt.savefig("pricing_data_visualization.png", dpi=300, bbox_inches="tight")
    plt.show()



# Visualize the historical data
plot_historical_analysis(history_df)
The time series plot shows trends over time, while the correlation plot reveals if there’s a linear relationship between price and demand. A negative correlation suggests demand decreases as price increases (price sensitivity).

Example Output: Historical Data Analysis

Historical Pricing Data Analysis The visualizations above show:
  • Top panels: How unit price and sales volume have varied over 2 years of weekly data
  • Bottom panel: A clear negative correlation (-0.64) between price and sales, confirming price sensitivity

Step 3: Set Up Pricing Simulation

Now, let’s define the range of prices we want to test. We’ll create a range of 11 price points ranging from 85% to 115% of your historical average price. We’ll call this the “base” price, and use it as a basline for comparison.
# Calculate base price from historical data
base_price = history_df["unit_price"].mean()

# Create price range (11 price points from 85% to 115% of base price)
price_simulation_range = np.linspace(base_price * 0.85, base_price * 1.15, 11)

print(f"Testing prices from ${price_simulation_range[0]:.2f} to ${price_simulation_range[-1]:.2f}")
Adjust the range: You can modify the 0.85 and 1.15 multipliers to test a wider or narrower price range. For example, use 0.7 and 1.3 to test 70% to 130% of the base price.

Step 4: Prepare Data for Forecasting

For each price point, we need to create a separate forecast scenario. We’ll duplicate the future DataFrame and modify the unit_price column for each scenario.
# Data preparation for pricing simulation
target_dfs = []
for price in price_simulation_range:
    modified_future = future_df.copy()
    modified_future["unit_price"] = price
    target_dfs.append(modified_future)

Step 5: Run AI Forecasts

async def get_forecasts():
    async with SynthefyAsyncAPIClient() as api_client:
        results = await api_client.forecast_dfs(
            history_dfs=[history_df] * len(price_simulation_range),
            target_dfs=target_dfs,
            target_col="sales",
            timestamp_col="date",
            metadata_cols=["unit_price"],
            leak_cols=["unit_price"],
            model="sfm-moe-v1",
        )
        return results


# Run the async forecast function
results = asyncio.run(get_forecasts())
print(f"✓ Received {len(results)} forecast results")
Key parameters:
  • metadata_cols=["unit_price"]: Features the model can use
  • leak_cols=["unit_price"]: Features that are known in advance (price is controllable)

Step 6: Create Time Series Forecast Visualization

First, let’s create a comprehensive time series visualization that shows all forecast scenarios overlaid on the historical data.
def plot_forecast_time_series(
    history_df,
    future_df,
    results,
    price_simulation_range,
    base_price,
    optimal_price,
):
    """Plot historical data and all forecasts for different prices using swarm-visualizer."""
    fig, ax = plt.subplots(figsize=(20, 6))

    # Get future dates
    future_dates = future_df["date"].tolist()

    # Prepare data for swarm-visualizer - show only last 50% of history
    history_cutoff = len(history_df) // 2
    history_subset = history_df.iloc[history_cutoff:]

    normalized_dict = {
        "Historical Sales": {
            "x": history_subset["date"],
            "y": history_subset["sales"],
            "lw": 3,
            "linestyle": "-",
            "color": "black",
            "alpha": 0.8,
            "zorder": 5,
        }
    }

    # Plot ALL forecasts - every single price point!
    # Orange gradient color scheme for all forecasts
    import matplotlib.cm as cm

    # Create orange gradient colors for all price points
    orange_colors = cm.Oranges(
        np.linspace(0.3, 0.9, len(price_simulation_range))
    )

    for i, (price, result) in enumerate(zip(price_simulation_range, results)):
        forecast_values = result["sales"].tolist()

        # Combine historical data with forecast for this price
        # Historical data (same for all prices) - only last 50%
        historical_dates = history_subset["date"].tolist()
        historical_sales = history_subset["sales"].tolist()

        # Combine historical + forecast dates and values
        combined_dates = historical_dates + future_dates
        combined_sales = historical_sales + forecast_values

        # Determine styling based on price type - all using orange gradient
        color = orange_colors[i]  # Each price gets its own orange shade

        if abs(price - optimal_price) < 0.01:
            label = f"Optimal Price (${price:.2f})"
            linewidth = 4
            alpha = 1.0
        elif abs(price - base_price) < 0.01:
            label = f"Base Price (${price:.2f})"
            linewidth = 4
            alpha = 1.0
        else:
            label = f"${price:.2f}"
            linewidth = 2.0
            alpha = 0.8

        normalized_dict[label] = {
            "x": combined_dates,
            "y": combined_sales,
            "lw": linewidth,
            "linestyle": "-",
            "color": color,
            "alpha": alpha,
            "zorder": 4,
        }

    # Use swarm-visualizer plot_overlaid_lineplot
    swarm_visualizer.plot_overlaid_lineplot(
        ax=ax,
        normalized_dict=normalized_dict,
        title_str="Pricing Simulation: Complete Time Series with Forecasts by Price",
        ylabel="Sales",
        xlabel="Date",
        legend_present=True,
    )

    # Add light blue background for the forecast region
    last_historical_date = history_subset["date"].iloc[-1]
    last_forecast_date = future_df["date"].iloc[-1]

    # Add light blue background for the forecast region
    ax.axvspan(
        xmin=last_historical_date,
        xmax=last_forecast_date,
        ymin=0,
        ymax=1,
        color="lightblue",
        alpha=0.2,
        zorder=1,
        label="Forecast Region",
    )

    # Add light blue vertical line to separate historical from forecast
    ax.axvline(
        x=last_historical_date,
        color="lightblue",
        linestyle="-",
        alpha=0.8,
        linewidth=3,
        zorder=3,
    )
    ax.text(
        last_historical_date,
        ax.get_ylim()[1] * 0.95,
        "Forecasts",
        ha="left",
        va="top",
        fontsize=12,
        color="black",
        weight="bold",
        zorder=6,
    )

    # Use swarm-visualizer set_axis_infos for consistent styling
    from swarm_visualizer.utility import set_axis_infos
    set_axis_infos(
        ax=ax,
        xlabel="Date",
        ylabel="Sales",
        title_str="Simulation: Forecasts for Different Prices",
        grid=True,
    )
    # Rotate x-axis labels to prevent overlap and improve readability
    plt.setp(ax.get_xticklabels(), rotation=45, ha="right", fontsize=10)

    plt.savefig("forecast_time_series.png", dpi=300, bbox_inches="tight")
    plt.show()

# Create time series visualization showing all forecasts
print("📊 Creating time series visualization of all forecasts...")
plot_forecast_time_series(
    history_df,
    future_df,
    results,
    price_simulation_range,
    base_price,
    optimal_price,
)

Example Output: Time Series Forecast Visualization

Time Series Forecast Visualization This comprehensive visualization shows:
  • Black line: Historical sales data (last 50% for focus)
  • Orange gradient lines: All forecast scenarios for different prices
  • Light blue background: Highlights the forecast region
  • Optimal & Base prices: Thicker lines for key scenarios

Step 7: Analyze Results

Extract the forecasts and calculate revenue for each price point. Then identify the optimal price that maximizes revenue.
# Extract forecasts (mean sales for each price point)
forecasts = [int(round(result["sales"].mean())) for result in results]

# Calculate revenue for each price point
revenues = [price * forecast for price, forecast in zip(price_simulation_range, forecasts)]

# Find optimal price point
optimal_idx = np.argmax(revenues)
optimal_price = price_simulation_range[optimal_idx]
optimal_transactions = forecasts[optimal_idx]
optimal_revenue = revenues[optimal_idx]

# Find base price index (closest to current average price)
base_idx = np.argmin(np.abs(price_simulation_range - base_price))
base_transactions = forecasts[base_idx]
base_revenue = revenues[base_idx]

Step 8: Visualize Pricing Insights

Finally, let’s create comprehensive visualizations to understand the price-demand relationship and identify the optimal pricing strategy.
# Create 3-plot visualization
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(18, 5))
fig.suptitle(
    "Pricing Simulation Analysis", fontsize=18, fontweight="bold", y=0.95
)

# Plot 1: Price vs Sales (Partial Dependence Plot)
ax1.plot(
    price_simulation_range,
    forecasts,
    "x--",
    linewidth=2.5,
    markersize=8,
    color="#4A90E2",
    markeredgewidth=2,
    label="Forecasted Sales",
)
ax1.axvline(
    x=base_price,
    color="black",
    linestyle="--",
    linewidth=2,
    alpha=0.8,
    label="Base Price",
)
ax1.set_xlabel("Sales Price ($)", fontsize=11)
ax1.set_ylabel("Avg Forecasted Sales", fontsize=11)
ax1.set_title(
    "Partial Dependence Plot (Price vs Avg Forecasted Sales)", fontsize=12
)
ax1.grid(True, alpha=0.3, linestyle="-", linewidth=0.5)
ax1.legend(fontsize=9, loc="upper right")
ax1.spines["top"].set_visible(True)
ax1.spines["right"].set_visible(True)

# Plot 2: Price vs Revenue
ax2.plot(
    price_simulation_range,
    revenues,
    "o--",
    linewidth=2.5,
    markersize=7,
    color="#E24A90",
    markeredgewidth=1.5,
    markeredgecolor="white",
    label="Expected Revenue",
)
ax2.axvline(
    x=base_price,
    color="black",
    linestyle="--",
    linewidth=2,
    alpha=0.8,
    label="Base Price",
)
ax2.axvline(
    x=optimal_price,
    color="#2ECC71",
    linestyle="--",
    linewidth=2,
    alpha=0.8,
    label="Optimal Price",
)
ax2.scatter(
    [optimal_price],
    [optimal_revenue],
    color="#2ECC71",
    s=200,
    zorder=5,
    marker="o",
    edgecolors="white",
    linewidth=2,
)
ax2.set_xlabel("Sales Price ($)", fontsize=11)
ax2.set_ylabel("Expected Revenue ($)", fontsize=11)
ax2.set_title("Price vs Revenue Optimization", fontsize=12)
ax2.grid(True, alpha=0.3, linestyle="-", linewidth=0.5)
ax2.legend(fontsize=9)

# Format y-axis for revenue
ax2.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f"${x:,.0f}"))

# Plot 3: Revenue comparison bar chart
colors = ["#FF6B6B", "#51CF66"]
bars = ax3.bar(
    [
        "Base Price\n" + f"${base_price:.2f}",
        "Optimal Price\n" + f"${optimal_price:.2f}",
    ],
    [base_revenue, optimal_revenue],
    color=colors,
    alpha=0.8,
    edgecolor="black",
    linewidth=2,
)
ax3.set_ylabel("Expected Revenue ($)", fontsize=12, fontweight="bold")
ax3.set_title("Revenue Comparison", fontsize=14)
ax3.grid(True, alpha=0.3, axis="y")

# Add value labels on bars
for bar in bars:
    height = bar.get_height()
    ax3.text(
        bar.get_x() + bar.get_width() / 2.0,
        height,
        f"${height:,.0f}",
        ha="center",
        va="bottom",
        fontsize=11,
        fontweight="bold",
    )

ax3.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f"${x:,.0f}"))

plt.tight_layout()
plt.savefig("pricing_simulation_results.png", dpi=300, bbox_inches="tight")
plt.show()

Example Output: Pricing Simulation Results

Pricing Simulation Analysis Now you can answer critical business questions:
  1. How price-sensitive are my customers? → Look at Plot 1 (steeper slope = more sensitivity)
  2. What’s my optimal price? → Look at Plot 2 (green marker)
  3. How much revenue am I leaving on the table? → Look at Plot 3 (red vs green bars)

Complete Code

Here’s the full working example you can run:
import asyncio

import matplotlib.cm as cm
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import swarm_visualizer
from swarm_visualizer.utility import set_axis_infos
from synthefy.api_client import SynthefyAsyncAPIClient

# Step 1: Load Your Historical Sales Data
history_df = pd.read_csv(
    "https://drive.google.com/uc?export=download&id=1FtLW17XE1NHcV1bF8mLW_2WHVKzrHts_"
)
future_df = pd.read_csv(
    "https://drive.google.com/uc?export=download&id=1l2zG7GNTcdDm_HzKhhwii3GTI0Z99a9h"
)


def plot_historical_analysis(history_df):
    """Create two plots to analyze historical price and transaction data."""
    # --- Historical Time Series Plot ---
    fig, ax1 = plt.subplots(figsize=(13, 5))

    sns.lineplot(
        data=history_df, x="date", y="unit_price",
        marker="o", label="Unit Price", color="C0", ax=ax1,
    )
    ax1.set_ylabel("Unit Price", color="C0", fontsize=12)
    ax1.tick_params(axis="y", labelcolor="C0")
    ax1.set_xlabel("Date", fontsize=12)

    ax2 = ax1.twinx()
    sns.lineplot(
        data=history_df, x="date", y="sales",
        marker="s", label="Sales", color="C1", ax=ax2,
    )
    ax2.set_ylabel("Sales", color="C1", fontsize=12)
    ax2.tick_params(axis="y", labelcolor="C1")

    n_dates = len(history_df["date"])
    step = max(n_dates // 10, 1)
    xticks_locs = history_df["date"].iloc[::step]
    ax1.set_xticks(xticks_locs)
    ax1.set_xticklabels(xticks_locs, rotation=45, ha="right")

    lns1, labs1 = ax1.get_legend_handles_labels()
    lns2, labs2 = ax2.get_legend_handles_labels()
    ax2.legend(lns1 + lns2, labs1 + labs2, loc="upper left", frameon=True)

    ax1.set_title("Historical Unit Price and Sales Over Time",
                  fontsize=15, weight="semibold")
    plt.tight_layout()
    plt.show()

    # --- Correlation Plot ---
    corr_val = (
        history_df[["unit_price", "sales"]]
        .corr().loc["unit_price", "sales"]
    )

    plt.figure(figsize=(7, 6))
    sns.regplot(
        x="unit_price", y="sales", data=history_df,
        marker="o", color="C2",
        line_kws={"color": "C3", "lw": 2, "label": f"Corr={corr_val:.2f}"},
    )
    plt.xlabel("Unit Price ($)", fontsize=12)
    plt.ylabel("Sales", fontsize=12)
    plt.title("Correlation Between Unit Price and Sales",
              fontsize=15, weight="semibold")
    plt.legend()
    plt.grid(True, linestyle="--", alpha=0.4)
    plt.tight_layout()
    plt.show()


# Step 2: Visualize Historical Data
plot_historical_analysis(history_df)

# Step 3: Set Up Price Grid
base_price = history_df["unit_price"].mean()
price_simulation_range = np.linspace(base_price * 0.85, base_price * 1.15, 11)
print(f"Testing prices from ${price_simulation_range[0]:.2f} to ${price_simulation_range[-1]:.2f}")

# Step 4: Prepare Data for Forecasting
target_dfs = []
for price in price_simulation_range:
    modified_future = future_df.copy()
    modified_future["unit_price"] = price
    target_dfs.append(modified_future)


# Step 5: Run AI Forecasts
async def get_forecasts():
    async with SynthefyAsyncAPIClient() as api_client:
        results = await api_client.forecast_dfs(
            history_dfs=[history_df] * len(price_simulation_range),
            target_dfs=target_dfs,
            target_col="sales",
            timestamp_col="date",
            metadata_cols=["unit_price"],
            leak_cols=["unit_price"],
            model="sfm-moe-v1",
        )
        return results


results = asyncio.run(get_forecasts())
print(f"✓ Received {len(results)} forecast results")

# Step 6: Analyze Results
forecasts = [int(round(result["sales"].mean())) for result in results]
revenues = [price * forecast for price, forecast in zip(price_simulation_range, forecasts)]

optimal_idx = np.argmax(revenues)
optimal_price = price_simulation_range[optimal_idx]
optimal_revenue = revenues[optimal_idx]

base_idx = np.argmin(np.abs(price_simulation_range - base_price))
base_revenue = revenues[base_idx]

# Step 6: Create Time Series Forecast Visualization
def plot_forecast_time_series(
    history_df,
    future_df,
    results,
    price_simulation_range,
    base_price,
    optimal_price,
):
    """Plot historical data and all forecasts for different prices using swarm-visualizer."""
    fig, ax = plt.subplots(figsize=(20, 6))

    # Get future dates
    future_dates = future_df["date"].tolist()

    # Prepare data for swarm-visualizer - show only last 50% of history
    history_cutoff = len(history_df) // 2
    history_subset = history_df.iloc[history_cutoff:]

    normalized_dict = {
        "Historical Sales": {
            "x": history_subset["date"],
            "y": history_subset["sales"],
            "lw": 3,
            "linestyle": "-",
            "color": "black",
            "alpha": 0.8,
            "zorder": 5,
        }
    }

    # Plot ALL forecasts - every single price point!
    # Orange gradient color scheme for all forecasts
    # Create orange gradient colors for all price points
    orange_colors = cm.Oranges(
        np.linspace(0.3, 0.9, len(price_simulation_range))
    )

    for i, (price, result) in enumerate(zip(price_simulation_range, results)):
        forecast_values = result["sales"].tolist()

        # Combine historical data with forecast for this price
        # Historical data (same for all prices) - only last 50%
        historical_dates = history_subset["date"].tolist()
        historical_sales = history_subset["sales"].tolist()

        # Combine historical + forecast dates and values
        combined_dates = historical_dates + future_dates
        combined_sales = historical_sales + forecast_values

        # Determine styling based on price type - all using orange gradient
        color = orange_colors[i]  # Each price gets its own orange shade

        if abs(price - optimal_price) < 0.01:
            label = f"Optimal Price (${price:.2f})"
            linewidth = 4
            alpha = 1.0
        elif abs(price - base_price) < 0.01:
            label = f"Base Price (${price:.2f})"
            linewidth = 4
            alpha = 1.0
        else:
            label = f"${price:.2f}"
            linewidth = 2.0
            alpha = 0.8

        normalized_dict[label] = {
            "x": combined_dates,
            "y": combined_sales,
            "lw": linewidth,
            "linestyle": "-",
            "color": color,
            "alpha": alpha,
            "zorder": 4,
        }

    # Use swarm-visualizer plot_overlaid_lineplot
    swarm_visualizer.plot_overlaid_lineplot(
        ax=ax,
        normalized_dict=normalized_dict,
        title_str="Pricing Simulation: Complete Time Series with Forecasts by Price",
        ylabel="Sales",
        xlabel="Date",
        legend_present=True,
    )

    # Add light blue background for the forecast region
    last_historical_date = history_subset["date"].iloc[-1]
    last_forecast_date = future_df["date"].iloc[-1]

    # Add light blue background for the forecast region
    ax.axvspan(
        xmin=last_historical_date,
        xmax=last_forecast_date,
        ymin=0,
        ymax=1,
        color="lightblue",
        alpha=0.2,
        zorder=1,
        label="Forecast Region",
    )

    # Add light blue vertical line to separate historical from forecast
    ax.axvline(
        x=last_historical_date,
        color="lightblue",
        linestyle="-",
        alpha=0.8,
        linewidth=3,
        zorder=3,
    )
    ax.text(
        last_historical_date,
        ax.get_ylim()[1] * 0.95,
        "Forecasts",
        ha="left",
        va="top",
        fontsize=12,
        color="black",
        weight="bold",
        zorder=6,
    )

    # Use swarm-visualizer set_axis_infos for consistent styling
    set_axis_infos(
        ax=ax,
        xlabel="Date",
        ylabel="Sales",
        title_str="Simulation: Forecasts for Different Prices",
        grid=True,
    )
    # Rotate x-axis labels to prevent overlap and improve readability
    plt.setp(ax.get_xticklabels(), rotation=45, ha="right", fontsize=10)

    plt.savefig("forecast_time_series.png", dpi=300, bbox_inches="tight")
    plt.show()

# Create time series visualization showing all forecasts
print("📊 Creating time series visualization of all forecasts...")
plot_forecast_time_series(
    history_df,
    future_df,
    results,
    price_simulation_range,
    base_price,
    optimal_price,
)

# Step 7: Analyze Results
forecasts = [int(round(result["sales"].mean())) for result in results]
revenues = [price * forecast for price, forecast in zip(price_simulation_range, forecasts)]

optimal_idx = np.argmax(revenues)
optimal_price = price_simulation_range[optimal_idx]
optimal_revenue = revenues[optimal_idx]

base_idx = np.argmin(np.abs(price_simulation_range - base_price))
base_revenue = revenues[base_idx]

# Step 8: Visualize Pricing Insights
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(18, 5))
fig.suptitle("Pricing Simulation Analysis", fontsize=18, fontweight="bold", y=0.95)

# Plot 1: Price vs Sales
ax1.plot(price_simulation_range, forecasts, "x--", linewidth=2.5, markersize=8,
         color="#4A90E2", markeredgewidth=2, label="Forecasted Sales")
ax1.axvline(x=base_price, color="black", linestyle="--",
            linewidth=2, alpha=0.8, label="Base Price")
ax1.set_xlabel("Sales Price ($)", fontsize=11)
ax1.set_ylabel("Avg Forecasted Sales", fontsize=11)
ax1.set_title("Partial Dependence Plot (Price vs Avg Forecasted Sales)", fontsize=12)
ax1.grid(True, alpha=0.3, linestyle="-", linewidth=0.5)
ax1.legend(fontsize=9, loc="upper right")

# Plot 2: Price vs Revenue
ax2.plot(price_simulation_range, revenues, "o--", linewidth=2.5, markersize=7,
         color="#E24A90", markeredgewidth=1.5, markeredgecolor="white",
         label="Expected Revenue")
ax2.axvline(x=base_price, color="black", linestyle="--",
            linewidth=2, alpha=0.8, label="Base Price")
ax2.axvline(x=optimal_price, color="#2ECC71", linestyle="--",
            linewidth=2, alpha=0.8, label="Optimal Price")
ax2.scatter([optimal_price], [optimal_revenue], color="#2ECC71",
            s=200, zorder=5, marker="o", edgecolors="white", linewidth=2)
ax2.set_xlabel("Sales Price ($)", fontsize=11)
ax2.set_ylabel("Expected Revenue ($)", fontsize=11)
ax2.set_title("Price vs Revenue Optimization", fontsize=12)
ax2.grid(True, alpha=0.3, linestyle="-", linewidth=0.5)
ax2.legend(fontsize=9)
ax2.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f"${x:,.0f}"))

# Plot 3: Revenue Comparison
bars = ax3.bar(
    [f"Base Price\n${base_price:.2f}", f"Optimal Price\n${optimal_price:.2f}"],
    [base_revenue, optimal_revenue],
    color=["#FF6B6B", "#51CF66"], alpha=0.8, edgecolor="black", linewidth=2,
)
ax3.set_ylabel("Expected Revenue ($)", fontsize=12, fontweight="bold")
ax3.set_title("Revenue Comparison", fontsize=14)
ax3.grid(True, alpha=0.3, axis="y")

for bar in bars:
    height = bar.get_height()
    ax3.text(bar.get_x() + bar.get_width() / 2.0, height,
             f"${height:,.0f}", ha="center", va="bottom",
             fontsize=11, fontweight="bold")

ax3.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f"${x:,.0f}"))

plt.tight_layout()
plt.savefig("pricing_simulation_results.png", dpi=300, bbox_inches="tight")
plt.show()

Next Steps

  1. Prepare your own data with historical prices and transactions
  2. Run the simulation with different price ranges
  3. Analyze the results to find your optimal price point
Pro tip: Run pricing simulations regularly (monthly or quarterly) as market conditions change. Your optimal price today might not be optimal tomorrow!