S - Dynamic Pricing Strategy using Python

 

Dynamic Pricing Strategy using Python


Dynamic Pricing: Case Study


Download the dataset below to solve this Data Science case study on Dynamic Pricing.



A ride-sharing company is looking to adopt a dynamic pricing strategy that optimizes fares in real time based on current market conditions. At present, the company relies solely on ride duration to determine fares. However, by using data-driven techniques, the company aims to analyze historical data and develop a predictive model capable of adjusting prices dynamically in response to evolving factors.

The provided dataset comprises historical ride data, including features such as the number of riders, number of drivers, location category, customer loyalty status, number of past rides, average ratings, time of booking, vehicle type, expected ride duration, and historical ride costs.

Your objective is to create a dynamic pricing model that utilizes these features to predict the optimal fare for each ride in real time. The model must take into account elements such as demand patterns and supply availability.

Case Study References

Dynamic Pricing involves using data science to adjust the prices of products or services in real time, based on multiple factors. This approach helps companies optimize revenue by setting flexible prices that reflect market demand, customer behavior, and competitor pricing. If you're interested in learning how to build a data-driven dynamic pricing strategy, this article is designed for you. I will guide you through developing a Dynamic Pricing Strategy using Python.

Understanding Dynamic Pricing

Dynamic Pricing refers to adjusting the price of a product or service in real time according to various factors. Businesses use this approach to enhance revenue and profitability by establishing flexible prices that react to shifts in market demand, customer behaviors, and competitor actions. With the help of data-driven insights and machine learning algorithms, companies can update prices dynamically to achieve optimal outcomes.

For instance, imagine a ride-sharing service in a busy metropolitan area that wants to maximize revenue and boost customer satisfaction. Instead of using a fixed per-kilometer rate that fails to capture fluctuations in supply and demand, a dynamic pricing model would adjust fares based on factors like historical trip data, current demand levels, traffic conditions, and local events. During periods of high demand, such as rush hours or major events, the model could increase fares to encourage more drivers to participate, thereby balancing supply and demand. Conversely, during off-peak times, lower fares could help attract more riders.

Overview of the Dynamic Pricing Strategy

The goal of a dynamic pricing strategy is to maximize revenue and profitability by setting prices that reflect real-time market conditions. This strategy allows businesses to adjust prices dynamically based on factors such as:

  • Time of day
  • Day of the week
  • Customer segments
  • Inventory levels
  • Seasonal variations
  • Competitor pricing
  • Overall market conditions

To implement such a strategy, access to comprehensive data is crucial. You’ll need datasets that include historical sales data, customer purchase patterns, market demand forecasts, cost data, customer segmentation information, and real-time market data.

I have sourced an ideal dataset for this task. You can download it from here.

Dynamic Pricing Strategy using Python

Let's kick off the process of building our dynamic pricing strategy by importing the necessary Python libraries and loading the dataset:

Google Colab


!wget https://statso.io/wp-content/uploads/2023/06/dynamic_pricing.csv

--2025-02-03 09:45:28-- https://statso.io/wp-content/uploads/2023/06/dynamic_pricing.csv Resolving statso.io (statso.io)... 192.0.78.225, 192.0.78.171 Connecting to statso.io (statso.io)|192.0.78.225|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 66211 (65K) [text/csv] Saving to: ‘dynamic_pricing.csv.1’ dynamic_pricing.csv 100%[===================>] 64.66K --.-KB/s in 0.06s 2025-02-03 09:45:29 (1.08 MB/s) - ‘dynamic_pricing.csv.1’ saved [66211/66211]

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go

data = pd.read_csv("dynamic_pricing.csv")
print(data.head())

Number_of_Riders Number_of_Drivers Location_Category \ 0 90 45 Urban 1 58 39 Suburban 2 42 31 Rural 3 89 28 Rural 4 78 22 Rural Customer_Loyalty_Status Number_of_Past_Rides Average_Ratings \ 0 Silver 13 4.47 1 Silver 72 4.06 2 Silver 0 3.99 3 Regular 67 4.31 4 Regular 74 3.77 Time_of_Booking Vehicle_Type Expected_Ride_Duration \ 0 Night Premium 90 1 Evening Economy 43 2 Afternoon Premium 76 3 Afternoon Premium 134 4 Afternoon Economy 149 Historical_Cost_of_Ride 0 284.257273 1 173.874753 2 329.795469 3 470.201232 4 579.681422


Exploratory Data Analysis


Let’s have a look at the descriptive statistics of the data:


print(data.describe())

Number_of_Riders Number_of_Drivers Number_of_Past_Rides \ count 1000.000000 1000.000000 1000.000000 mean 60.372000 27.076000 50.031000 std 23.701506 19.068346 29.313774 min 20.000000 5.000000 0.000000 25% 40.000000 11.000000 25.000000 50% 60.000000 22.000000 51.000000 75% 81.000000 38.000000 75.000000 max 100.000000 89.000000 100.000000 Average_Ratings Expected_Ride_Duration Historical_Cost_of_Ride count 1000.000000 1000.00000 1000.000000 mean 4.257220 99.58800 372.502623 std 0.435781 49.16545 187.158756 min 3.500000 10.00000 25.993449 25% 3.870000 59.75000 221.365202 50% 4.270000 102.00000 362.019426 75% 4.632500 143.00000 510.497504 max 5.000000 180.00000 836.116419


Now let’s have a look at the relationship between expected ride duration and the historical cost of the ride:


fig = px.scatter(data, x='Expected_Ride_Duration',
                 y='Historical_Cost_of_Ride',
                 title='Expected Ride Duration vs. Historical Cost of Ride',
                 trendline='ols')
fig.show()



Now let’s have a look at the distribution of the historical cost of rides based on the vehicle type:


fig = px.box(data, x='Vehicle_Type',
             y='Historical_Cost_of_Ride',
             title='Historical Cost of Ride Distribution by Vehicle Type')
fig.show()



Now let’s have a look at the correlation matrix:

# Select only numerical features for correlation calculation
numerical_data = data.select_dtypes(include=['number'])  # Selects columns with numerical data types

# Calculate the correlation matrix
corr_matrix = numerical_data.corr()

# Create the heatmap
fig = go.Figure(data=go.Heatmap(z=corr_matrix.values,
                                x=corr_matrix.columns,
                                y=corr_matrix.columns,
                                colorscale='Viridis'))
fig.update_layout(title='Correlation Matrix')
fig.show()




Implementing a Dynamic Pricing Strategy

The company’s current pricing model relies solely on the expected ride duration to set ride fares. To optimize revenue and better respond to market fluctuations, we are now implementing a dynamic pricing strategy. This new approach will adjust ride fares in real time by taking into account both demand and supply. Prices will be increased during periods of high demand or low driver availability, and decreased when demand is low or there is an abundance of drivers.

Below is an example of how to implement this dynamic pricing strategy using Python:


import numpy as np

# Calculate demand_multiplier based on percentile for high and low demand
high_demand_percentile = 75
low_demand_percentile = 25

data['demand_multiplier'] = np.where(data['Number_of_Riders'] > np.percentile(data['Number_of_Riders'], high_demand_percentile),
                                     data['Number_of_Riders'] / np.percentile(data['Number_of_Riders'], high_demand_percentile),
                                     data['Number_of_Riders'] / np.percentile(data['Number_of_Riders'], low_demand_percentile))

# Calculate supply_multiplier based on percentile for high and low supply
high_supply_percentile = 75
low_supply_percentile = 25

data['supply_multiplier'] = np.where(data['Number_of_Drivers'] > np.percentile(data['Number_of_Drivers'], low_supply_percentile),
                                     np.percentile(data['Number_of_Drivers'], high_supply_percentile) / data['Number_of_Drivers'],
                                     np.percentile(data['Number_of_Drivers'], low_supply_percentile) / data['Number_of_Drivers'])

# Define price adjustment factors for high and low demand/supply
demand_threshold_high = 1.2  # Higher demand threshold
demand_threshold_low = 0.8  # Lower demand threshold
supply_threshold_high = 0.8  # Higher supply threshold
supply_threshold_low = 1.2  # Lower supply threshold

# Calculate adjusted_ride_cost for dynamic pricing
data['adjusted_ride_cost'] = data['Historical_Cost_of_Ride'] * (
    np.maximum(data['demand_multiplier'], demand_threshold_low) *
    np.maximum(data['supply_multiplier'], supply_threshold_high)
)


In the previous section, we computed the demand multiplier by comparing the number of riders to predetermined high and low demand percentiles. If the rider count exceeds the high-demand percentile, the multiplier is calculated as the number of riders divided by this high-demand percentile. Alternatively, if the rider count is below the low-demand percentile, the multiplier becomes the number of riders divided by the low-demand percentile.

Similarly, for supply, we determined the supply multiplier by comparing the number of drivers to set high and low supply percentiles. If the driver count is above the low-supply percentile, the multiplier is computed as the high-supply percentile divided by the number of drivers. If the driver count is below the low-supply percentile, it is calculated as the low-supply percentile divided by the number of drivers.

The adjusted ride cost for our dynamic pricing model is then computed by taking the historical ride cost and multiplying it by two factors:

  1. The maximum of the demand multiplier and a specified lower threshold (demand_threshold_low).
  2. The maximum of the supply multiplier and an upper threshold (supply_threshold_high).

These multiplications ensure that the final ride cost accurately reflects the influence of both demand and supply while using thresholds to avoid extreme adjustments.

Now, let’s compute the profit percentage resulting from the implementation of this dynamic pricing strategy:


# Calculate the profit percentage for each ride
data['profit_percentage'] = ((data['adjusted_ride_cost'] - data['Historical_Cost_of_Ride']) / data['Historical_Cost_of_Ride']) * 100
# Identify profitable rides where profit percentage is positive
profitable_rides = data[data['profit_percentage'] > 0]

# Identify loss rides where profit percentage is negative
loss_rides = data[data['profit_percentage'] < 0]


import plotly.graph_objects as go

# Calculate the count of profitable and loss rides
profitable_count = len(profitable_rides)
loss_count = len(loss_rides)

# Create a donut chart to show the distribution of profitable and loss rides
labels = ['Profitable Rides', 'Loss Rides']
values = [profitable_count, loss_count]

fig = go.Figure(data=[go.Pie(labels=labels, values=values, hole=0.4)])
fig.update_layout(title='Profitability of Rides (Dynamic Pricing vs. Historical Pricing)')
fig.show()



Now let’s have a look at the relationship between the expected ride duration and the cost of the ride based on the dynamic pricing strategy:

fig = px.scatter(data,
                 x='Expected_Ride_Duration',
                 y='adjusted_ride_cost',
                 title='Expected Ride Duration vs. Cost of Ride',
                 trendline='ols')
fig.show()




Training a Predictive Model


Now, as we have implemented a dynamic pricing strategy, let’s train a Machine Learning model. Before training the model, let’s preprocess the data:


import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler

def data_preprocessing_pipeline(data):
    #Identify numeric and categorical features
    numeric_features = data.select_dtypes(include=['float', 'int']).columns
    categorical_features = data.select_dtypes(include=['object']).columns

    #Handle missing values in numeric features
    data[numeric_features] = data[numeric_features].fillna(data[numeric_features].mean())

    #Detect and handle outliers in numeric features using IQR
    for feature in numeric_features:
        Q1 = data[feature].quantile(0.25)
        Q3 = data[feature].quantile(0.75)
        IQR = Q3 - Q1
        lower_bound = Q1 - (1.5 * IQR)
        upper_bound = Q3 + (1.5 * IQR)
        data[feature] = np.where((data[feature] < lower_bound) | (data[feature] > upper_bound),
                                 data[feature].mean(), data[feature])

    #Handle missing values in categorical features
    data[categorical_features] = data[categorical_features].fillna(data[categorical_features].mode().iloc[0])

    return data

In the above code, we have implemented a data preprocessing pipeline to preprocess the data. You can learn more about it here. As vehicle type is a valuable factor, let’s convert it into a numerical feature before moving forward:

data["Vehicle_Type"] = data["Vehicle_Type"].map({"Premium": 1,
                                           "Economy": 0})

Now let’s split the data and train a Machine Learning model to 
predict the cost of a ride:

#splitting data
from sklearn.model_selection import train_test_split
x = np.array(data[["Number_of_Riders", "Number_of_Drivers", "Vehicle_Type", "Expected_Ride_Duration"]])
y = np.array(data[["adjusted_ride_cost"]])

x_train, x_test, y_train, y_test = train_test_split(x,
                                                    y,
                                                    test_size=0.2,
                                                    random_state=42)

# Reshape y to 1D array
y_train = y_train.ravel()
y_test = y_test.ravel()

# Training a random forest regression model
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor()
model.fit(x_train, y_train)


Now let’s test this Machine Learning model using some input values:

def get_vehicle_type_numeric(vehicle_type):
    vehicle_type_mapping = {
        "Premium": 1,
        "Economy": 0
    }
    vehicle_type_numeric = vehicle_type_mapping.get(vehicle_type)
    return vehicle_type_numeric
 
# Predicting using user input values
def predict_price(number_of_riders, number_of_drivers, vehicle_type, Expected_Ride_Duration):
    vehicle_type_numeric = get_vehicle_type_numeric(vehicle_type)
    if vehicle_type_numeric is None:
        raise ValueError("Invalid vehicle type")
   
    input_data = np.array([[number_of_riders, number_of_drivers, vehicle_type_numeric, Expected_Ride_Duration]])
    predicted_price = model.predict(input_data)
    return predicted_price

# Example prediction using user input values
user_number_of_riders = 50
user_number_of_drivers = 25
user_vehicle_type = "Economy"
Expected_Ride_Duration = 30
predicted_price = predict_price(user_number_of_riders, user_number_of_drivers, user_vehicle_type, Expected_Ride_Duration)
print("Predicted price:", predicted_price)

Predicted price: [255.11066489]

Here’s a comparison of the actual and predicted results:

import plotly.graph_objects as go

# Predict on the test set
y_pred = model.predict(x_test)

# Create a scatter plot with actual vs predicted values
fig = go.Figure()

fig.add_trace(go.Scatter(
    x=y_test.flatten(),
    y=y_pred,
    mode='markers',
    name='Actual vs Predicted'
))

# Add a line representing the ideal case
fig.add_trace(go.Scatter(
    x=[min(y_test.flatten()), max(y_test.flatten())],
    y=[min(y_test.flatten()), max(y_test.flatten())],
    mode='lines',
    name='Ideal',
    line=dict(color='red', dash='dash')
))

fig.update_layout(
    title='Actual vs Predicted Values',
    xaxis_title='Actual Values',
    yaxis_title='Predicted Values',
    showlegend=True,
)

fig.show()



Google Colab Code:



Comments

Popular posts from this blog

>QueueBite — AI-Powered Restaurant Waitlist Manager